0

我正在尝试对动态数据库做一些事情。我的要求如下:

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

=> 这些数据库供用户为所欲为。但是,每个数据库都是使用受限权限创建的,用户不能超越自己的数据库。

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

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

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

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

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

  3. 用户可以更改分隔符并编写复杂的内容,例如程序等,因此我无法使用分号“;”进行命令级别拆分 并一一执行。

我对 Yii 的尝试如下:

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

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

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

  4. 如果“执行”也抛出异常,我也会回滚这个事务,并将结果显示给用户。

我期待我的方法 2 在这种情况下有效。但是,我发现在“创建表命令”的情况下事务无法正常工作。在这里,“queryAll”命令给出了异常,但仍然继续创建用户表。现在,当我尝试使用“执行”再次运行相同的命令时,它再次给出异常,因为表已经存在。我很惊讶事务回滚没有任何区别。

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

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

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

  3. 我的方法有什么漏洞,谁能搞砸这个系统?

我为这么长的帖子道歉。然而,简单地解释这个问题有点困难。我在这篇文章中附上了我的 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";
    }
}
}

问候,卡皮尔

4

2 回答 2

2

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

1-用户可以访问它

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

所以你需要 Restful Api

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

于 2013-06-03T13:32:04.800 回答
1

这里有几个问题,所以让我总结一下我认为你在问的问题:

  1. 为什么创建表事务不回滚?
  2. 哪个活动记录函数可以正确处理选择或非选择查询?
  3. 如何处理多语句查询?

所以这是我的答案:

  1. MySQL 不使用事务保护 DDL 语句。这些是 create、alter、grant 等语句。PostgreSQL 可以,所以要么切换到 PostgreSQL,要么不尝试使用事务来保护 DDL。鉴于您不知道,在不解析用户的 SQL 的情况下,切换到 PostgreSQL 可能是您最好的选择,但请保留它,直到您阅读进一步的答案。

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

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

希望这可以帮助。

于 2013-06-04T00:58:38.500 回答