我正在尝试对动态数据库做一些事情。我的要求如下:
-- 需要动态生成和动态数据库。
=> 这些数据库供用户为所欲为。但是,每个数据库都是使用受限权限创建的,用户不能超越自己的数据库。
-- 需要对该数据库执行任何随机用户查询(或一组用户查询)
=> 鉴于用户可以编写任何查询,我不知道该查询是“选择”查询还是“非选择”查询。受影响”。这相当于“运行 mysql 脚本文件”
现在,我编写了一个 DatabaseManager 类,它为我执行“1”,但在实现“2”时我仍然面临问题。关键问题在于以下几点:
我不知道用户执行的查询。
我需要处理查询并将适当的结果返回给用户(这非常重要),即——对于选择查询,它应该返回用户请求的数据——对于非选择查询,它应该返回“受影响的行数”——对于复合查询(包含选择和非选择查询),它可以返回“受影响的行数”
用户可以更改分隔符并编写复杂的内容,例如程序等,因此我无法使用分号“;”进行命令级别拆分 并一一执行。
我对 Yii 的尝试如下:
我为每个查询(或我得到的一堆查询)创建了一个事务。
不知道查询是什么,我总是在上面执行“queryAll”。如果它是“基于选择的查询”,那么我希望从中得到正确的结果。如果成功,那么我提交事务
如果是“非选择”查询,我会在这里得到一个异常,我会回滚事务。在这种情况下,我接下来将创建一个新事务,然后调用“执行”命令。鉴于这是一个非 sql 或复合查询,我希望该命令能够运行。它应该只在出现语法错误或违反某些约束的情况下抛出异常
如果“执行”也抛出异常,我也会回滚这个事务,并将结果显示给用户。
我期待我的方法 2 在这种情况下有效。但是,我发现在“创建表命令”的情况下事务无法正常工作。在这里,“queryAll”命令给出了异常,但仍然继续创建用户表。现在,当我尝试使用“执行”再次运行相同的命令时,它再次给出异常,因为表已经存在。我很惊讶事务回滚没有任何区别。
现在,我的上述方法可能非常不正确,我很高兴听到一些关于什么是解决这个问题的正确方法的反馈。现在,我有一些关键问题:
是否可以在 Yii 中执行一堆随机的 sql 查询?更重要的是,是否可以将查询分解为单个查询?(我知道这是一个棘手的问题,也是 stackoverflow 本身讨论的一个大话题,但仍然问同样的问题)
为什么事务回滚不起作用?
我的方法有什么漏洞,谁能搞砸这个系统?
我为这么长的帖子道歉。然而,简单地解释这个问题有点困难。我在这篇文章中附上了我的 DatabaseManager 类的源代码,以供详细参考。
<?php
class DatabaseManager
{
public $db_name;
public $username;
public $password;
const HOST = "localhost";
const ROOT = "dummy_root";
const ROOT_PASSWORD = "dummy_root_password";
const DB_PASSWORD_SALT = 'dummy_password_salt';
public function getDbComponent($db_name) {
//if the component already exists then create it
if(Yii::app()->hasComponent($db_name)) {
$component = Yii::app()->getComponent($db_name);
return $component;
}
//if the db component doesn't exist, then create it and return the
//new component
try {
$this->db_name = $db_name;
$this->username = "dummy_user";
$this->password = "dummy_password";
$component = $this->createDbComponent();
Yii::app()->setComponent($db_name, $component);
} catch (PDOException $e) {
die("DB ERROR: ". $e->getMessage());
}
return $component;
}
private function createDbComponent() {
$pdo = new PDO("mysql:host=".self::HOST, self::ROOT, self::ROOT_PASSWORD, array(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,));
$pdo->exec("CREATE DATABASE IF NOT EXISTS `$this->db_name`;
GRANT ALL PRIVILEGES ON ".$this->db_name.".* TO '".$this->username."'@'localhost' IDENTIFIED BY '".$this->password."';
FLUSH PRIVILEGES;")
or die(print_r($pdo->errorInfo(), true));
$component = Yii::createComponent(array(
'class'=>'CDbConnection',
'connectionString'=>'mysql:host=localhost;dbname='.$this->db_name,
'emulatePrepare' => true,
'password'=> $this->password,
'username'=> $this->username,
'charset' => 'utf8',
'enableParamLogging' => true,
)
);
return $component;
}
private function formatErrorMessage($msg) {
$check_for_prefix = "CDbCommand failed to execute the SQL statement:";
$result = str_replace($check_for_prefix, "", $msg);
return $result;
}
private function executeSelectQuery($query, $db_name) {
//Get the db component on which the query should be executed
$db = $this->getDbComponent($db_name);
$command = $db->createCommand($query);
$transaction = $db->beginTransaction();
try {
$command = $db->createCommand($query);
$output = $command->queryAll();
$output = $command->queryAll();
$transaction->commit();
} catch (exception $e) {
$transaction->rollback();
//throw back this exception
//as it should be handled by the higher level code
throw new Exception($e->getMessage());
}
return $output;
}
private function executeNonSelectQuery($query, $db_name) {
//Get the db component on which the query should be executed
$db = $this->getDbComponent($db_name);
$transaction = $db->beginTransaction();
try {
$command = $db->createCommand($query);
$command->prepare();
$output = $command->execute();
$transaction->commit();
} catch (exception $e) {
$transaction->rollback();
//throw back this exception
//as it should be handled by the higher level code
throw new Exception($e->getMessage());
}
return $output;
}
public function runQuery($code, $db_name) {
try {
//we can have two kind of queries -- select or non-select
//since we don't know the type of query, we should by default try to make a select query
//if this gives an exception, then there is a possibility that this is a non-select query
//so now we try doing an "execute" on this
//if that goes fine, it means that the query was a non-sql query
//else we show the exception to user
$db = $this->getDbComponent($db_name);
try {
$output = $this->executeSelectQuery($code, $db_name);
$result['status'] = "success";
$result['output'] = $output;
} catch (exception $e) {
try {
$output = $this->executeNonSelectQuery($code, $db_name);
$result['status'] = "success";
$result['output'] = "Query OK, ".$output." row(s) affected";
} catch (exception $e) {
$error = $this->formatErrorMessage($e->getMessage());
$result['status'] = "failure";
$result['error'] = $error;
}
}
} catch (exception $e) {
$result = $e->getMessage();
}
return $result;
}
//resets the db
public function resetDbComponent($db_name) {
//in case of reset simply delete the database
$result = $this->deleteDbComponent($db_name);
if(!$this->getDbComponent($db_name)) {
throw new Exception("Could not create database -- ".$db_name);
} else {
$result['status'] = "success";
$result['output'] = "Database reset to inital state";
}
}
public function deleteDbComponent($db_name) {
$pdo = new PDO("mysql:host=".self::HOST, self::ROOT, self::ROOT_PASSWORD);
try {
$pdo->exec("DROP DATABASE IF EXISTS `$db_name`;");
Yii::app()->setComponent($db_name, null);
$result['status'] = 'failure';
$result['output'] = "Database ".$db_name." deleted";
} catch (exception $e) {
$result['status'] = 'failure';
$result['output'] = "Database ".$db_name." could not be deleted";
}
}
}
问候,卡皮尔