在动态数据库上运行用户Sql命令

时间:2013-06-02 15:55:37

标签: php mysql yii

我正在尝试使用动态数据库。我的要求如下:

- 需要动态生成和动态数据库。

=>这些数据库供用户随心所欲。但是,每个数据库都是使用受限制的权限创建的,用户无法超越自己的数据库。

- 需要在该数据库上执行任何随机用户查询(或一组用户查询)

=>鉴于用户可以编写任何查询,我不知道查询是“查询”查询还是“非选择”查询。受影响“。这相当于”运行mysql脚本文件“

现在,我编写了一个DatabaseManager类,它为我做了“1”,但我仍然面临着实现“2”的问题。关键问题在于以下几点:

  1. 我不知道用户执行的查询。

  2. 我需要处理查询并将适当的结果还原给用户(这非常重要),即 - 对于选择查询,它应该返回用户请求的数据 - 对于非选择查询,它应返回“受影响的行数” - 对于复合查询(包含选择和非选择查询),它可以返回“受影响的行数”

  3. 用户可以更改分隔符并编写复杂的东西,如程序等,所以我不能用分号“;”进行命令级别分割。并逐一执行。

  4. 我和Yii一起尝试的是:

    1. 我为每个查询(或我得到的一堆查询)创建了一个事务。

    2. 不知道查询是什么,我总是执行“queryAll”。如果它是“基于选择的查询”,那么我希望从中获得正确的结果。如果成功,那么我提交交易

    3. 如果是“非选择”查询,我会在这里得到一个例外,我将回滚该事务。在这种情况下,我接下来将创建一个新事务,然后调用“execute”命令。鉴于这是一个非sql或复合查询,我希望命令运行。它只应在语法错误或某些约束违规的情况下抛出异常

    4. 如果“执行”也引发异常,我也会回滚此事务,并将结果显示给用户。

    5. 我期待我的方法2在这种情况下工作。但是,我发现在“create table command”的情况下,事务处理不正常。这里,“queryAll”命令给出和异常,但仍然继续创建用户表。现在,当我尝试使用“execute”再次运行相同的命令时,它再次给出异常,因为该表已经存在。令我感到惊讶的是,事务回滚没有任何区别。

      现在,我的上述方法可能非常不正确,我很乐意听到一些关于解决这个问题的正确方法的反馈。现在,我有一些关键问题:

      1. 是否有可能在Yii中执行一堆随机sql查询?更重要的是,是否可以将查询分解为单个查询? (我知道这是一个棘手的问题,也是stackoverflow本身讨论的一个重要话题,但同样问同样的问题)

      2. 为什么事务回滚不起作用?

      3. 我的方法有哪些漏洞,以及如何搞砸这个系统?

      4. 我为这么长的帖子道歉。但是,简要解释这个问题有点困难。我正在使用这篇文章附上我的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";
            }
        }
        }
        

        此致 卡皮尔

2 个答案:

答案 0 :(得分:2)

我从您的问题中了解到您需要为您的数据库构建API:

1-用户可以访问它

2-可以做一些基本和复杂的SQL查询

所以你需要Restful Api

本文将为您提供帮助,您可以在Google上搜索更多相关信息 http://www.9lessons.info/2012/05/create-restful-services-api-in-php.html

答案 1 :(得分:1)

这里有几个问题,所以让我简单总结一下我的想法:

  1. 为什么创建表事务不会回滚?
  2. 哪个有效记录功能可以处理选择或非选择查询?
  3. 如何处理多语句查询?
  4. 所以这是我的答案:

    1. MySQL不会通过事务保护DDL sttatements。这些是创建,更改,授权等声明。 PostgreSQL会这样做,所以要么你切换到PostgreSQL,要么你不试图用事务来保护你的DDL。鉴于您不知道,如果不解析用户的SQL,可能是切换到PostgreSQL是您在这里的最佳选择,但保留这一点直到您阅读更多答案。

    2. 我认为Active Record不是为了做你想要的,而是试图将你的方形钉子敲入圆形的ActiveRecord洞可能非常困难。相反,我会使用Yii和ActiveRecord来管理您的用户和数据库列表,但使用普通的旧PHP来管理用户与这些数据库的相互作用。这意味着您有两个连接,一个连接到包含用户和数据库信息的管理数据库,另一个连接到用户的数据库。您可以使用mysql函数连接,查询和断开用户数据库。

    3. 我建议允许在单个查询中执行多个语句是非常不安全的。甚至可能是mysql函数不允许它。在任何情况下你都不应该允许它,因为这使得黑客能够执行一些更多的insidius SQL注入攻击,比如通过粘贴更改语句;从表中删除到用于构建sql语句的字段。除了定义程序,您不应该允许这样做。这意味着你需要至少构建一个基本的解析器。至少足以知道你正在执行什么声明。鉴于此,您甚至可以动态地了解事务支持是否可用,以及基于此提交或回滚。

    4. 希望这有帮助。