我正在尝试使用动态数据库。我的要求如下:
- 需要动态生成和动态数据库。
=>这些数据库供用户随心所欲。但是,每个数据库都是使用受限制的权限创建的,用户无法超越自己的数据库。
- 需要在该数据库上执行任何随机用户查询(或一组用户查询)
=>鉴于用户可以编写任何查询,我不知道查询是“查询”查询还是“非选择”查询。受影响“。这相当于”运行mysql脚本文件“
现在,我编写了一个DatabaseManager类,它为我做了“1”,但我仍然面临着实现“2”的问题。关键问题在于以下几点:
我不知道用户执行的查询。
我需要处理查询并将适当的结果还原给用户(这非常重要),即 - 对于选择查询,它应该返回用户请求的数据 - 对于非选择查询,它应返回“受影响的行数” - 对于复合查询(包含选择和非选择查询),它可以返回“受影响的行数”
用户可以更改分隔符并编写复杂的东西,如程序等,所以我不能用分号“;”进行命令级别分割。并逐一执行。
我和Yii一起尝试的是:
我为每个查询(或我得到的一堆查询)创建了一个事务。
不知道查询是什么,我总是执行“queryAll”。如果它是“基于选择的查询”,那么我希望从中获得正确的结果。如果成功,那么我提交交易
如果是“非选择”查询,我会在这里得到一个例外,我将回滚该事务。在这种情况下,我接下来将创建一个新事务,然后调用“execute”命令。鉴于这是一个非sql或复合查询,我希望命令运行。它只应在语法错误或某些约束违规的情况下抛出异常
如果“执行”也引发异常,我也会回滚此事务,并将结果显示给用户。
我期待我的方法2在这种情况下工作。但是,我发现在“create table command”的情况下,事务处理不正常。这里,“queryAll”命令给出和异常,但仍然继续创建用户表。现在,当我尝试使用“execute”再次运行相同的命令时,它再次给出异常,因为该表已经存在。令我感到惊讶的是,事务回滚没有任何区别。
现在,我的上述方法可能非常不正确,我很乐意听到一些关于解决这个问题的正确方法的反馈。现在,我有一些关键问题:
是否有可能在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";
}
}
}
此致 卡皮尔
答案 0 :(得分:2)
我从您的问题中了解到您需要为您的数据库构建API:
1-用户可以访问它
2-可以做一些基本和复杂的SQL查询
所以你需要Restful Api
本文将为您提供帮助,您可以在Google上搜索更多相关信息 http://www.9lessons.info/2012/05/create-restful-services-api-in-php.html
答案 1 :(得分:1)
这里有几个问题,所以让我简单总结一下我的想法:
所以这是我的答案:
MySQL不会通过事务保护DDL sttatements。这些是创建,更改,授权等声明。 PostgreSQL会这样做,所以要么你切换到PostgreSQL,要么你不试图用事务来保护你的DDL。鉴于您不知道,如果不解析用户的SQL,可能是切换到PostgreSQL是您在这里的最佳选择,但保留这一点直到您阅读更多答案。
我认为Active Record不是为了做你想要的,而是试图将你的方形钉子敲入圆形的ActiveRecord洞可能非常困难。相反,我会使用Yii和ActiveRecord来管理您的用户和数据库列表,但使用普通的旧PHP来管理用户与这些数据库的相互作用。这意味着您有两个连接,一个连接到包含用户和数据库信息的管理数据库,另一个连接到用户的数据库。您可以使用mysql函数连接,查询和断开用户数据库。
我建议允许在单个查询中执行多个语句是非常不安全的。甚至可能是mysql函数不允许它。在任何情况下你都不应该允许它,因为这使得黑客能够执行一些更多的insidius SQL注入攻击,比如通过粘贴更改语句;从表中删除到用于构建sql语句的字段。除了定义程序,您不应该允许这样做。这意味着你需要至少构建一个基本的解析器。至少足以知道你正在执行什么声明。鉴于此,您甚至可以动态地了解事务支持是否可用,以及基于此提交或回滚。
希望这有帮助。