From a97f43b03ee32480fd968710d27e74dacd5a6efe Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 26 Dec 2011 23:12:05 -0500 Subject: [PATCH] ... --- framework/db/dao/Command.php | 8 +- framework/db/dao/Connection.php | 3 +- framework/db/dao/Query.php | 9 +- framework/db/dao/QueryBuilder.php | 79 ++++++----- tests/unit/framework/db/dao/QueryTest.php | 215 ++++++++++++++++++++++++++++++ 5 files changed, 274 insertions(+), 40 deletions(-) create mode 100644 tests/unit/framework/db/dao/QueryTest.php diff --git a/framework/db/dao/Command.php b/framework/db/dao/Command.php index f049f7b..4e3e897 100644 --- a/framework/db/dao/Command.php +++ b/framework/db/dao/Command.php @@ -104,7 +104,7 @@ class Command extends \yii\base\Component */ public function setSql($value) { - $this->_sql = $this->connection->expandTablePrefix($value); + $this->_sql = $value; $this->_params = array(); $this->cancel(); return $this; @@ -120,7 +120,7 @@ class Command extends \yii\base\Component public function prepare() { if ($this->pdoStatement == null) { - $sql = $this->getSql(); + $sql = $this->connection->expandTablePrefix($this->getSql()); try { $this->pdoStatement = $this->connection->pdo->prepare($sql); } catch (\Exception $e) { @@ -225,7 +225,7 @@ class Command extends \yii\base\Component */ public function execute($params = array()) { - $sql = $this->getSql(); + $sql = $this->connection->expandTablePrefix($this->getSql()); $this->_params = array_merge($this->_params, $params); if ($this->_params === array()) { $paramLog = ''; @@ -358,7 +358,7 @@ class Command extends \yii\base\Component private function queryInternal($method, $params, $fetchMode = null) { $db = $this->connection; - $sql = $this->getSql(); + $sql = $db->expandTablePrefix($this->getSql()); $this->_params = array_merge($this->_params, $params); if ($this->_params === array()) { $paramLog = ''; diff --git a/framework/db/dao/Connection.php b/framework/db/dao/Connection.php index a9855bc..cef4148 100644 --- a/framework/db/dao/Connection.php +++ b/framework/db/dao/Connection.php @@ -414,12 +414,11 @@ class Connection extends \yii\base\ApplicationComponent /** * Creates a command for execution. - * @param mixed $query the DB query to be executed. This can be: + * @param string|array|Query $query the DB query to be executed. This can be: * * - a string representing the SQL statement to be executed * - a [[Query]] object representing the SQL query * - an array that will be used to initialize [[Query]] - * - null (default) if the query needs to be built using query builder methods next. * @return Command the DB command */ public function createCommand($query = null) diff --git a/framework/db/dao/Query.php b/framework/db/dao/Query.php index a61be30..938d814 100644 --- a/framework/db/dao/Query.php +++ b/framework/db/dao/Query.php @@ -520,7 +520,7 @@ class Query extends \yii\base\Object */ public function insert($table, $columns) { - $this->operation = array('insert', $table, $columns); + $this->operation = array('insert', $table, $columns, array()); return $this; } @@ -536,7 +536,8 @@ class Query extends \yii\base\Object */ public function update($table, $columns, $condition = '', $params = array()) { - $this->operation = array('update', $table, $columns, $condition, $params); + $this->addParams($params); + $this->operation = array('update', $table, $columns, $condition, array()); return $this; } @@ -550,8 +551,8 @@ class Query extends \yii\base\Object */ public function delete($table, $condition = '', $params = array()) { - $this->operation = array('delete', $table, $condition, $params); - return $this; + $this->operation = array('delete', $table, $condition); + return $this->addParams($params); } /** diff --git a/framework/db/dao/QueryBuilder.php b/framework/db/dao/QueryBuilder.php index a5650bf..e4a7e9b 100644 --- a/framework/db/dao/QueryBuilder.php +++ b/framework/db/dao/QueryBuilder.php @@ -33,7 +33,15 @@ class QueryBuilder extends \yii\base\Object * @var Schema the database schema */ public $schema; + /** + * @var Query the Query object. This is set when calling [[build()]] to generate a non-SELECT SQL statement. + */ + private $_query; + /** + * Constructor. + * @param Schema $schema the database schema information + */ public function __construct($schema) { $this->connection = $schema->connection; @@ -46,10 +54,15 @@ class QueryBuilder extends \yii\base\Object */ public function build($query) { - if ($this->operation !== null) { - $method = array_shift($this->operation); - return call_user_func_array(array($this, $method), $this->operation); - } + // non-SELECT query + if ($query->operation !== null) { + $this->_query = $query; + $method = array_shift($query->operation); + $sql = call_user_func_array(array($this, $method), $query->operation); + $this->_query = null; + return $sql; + } + // SELECT query $clauses = array( $this->buildSelect($query), $this->buildFrom($query), @@ -61,7 +74,7 @@ class QueryBuilder extends \yii\base\Object $this->buildOrderBy($query), $this->buildLimit($query), ); - return $this->connection->expandTablePrefix(implode("\n", array_filter($clauses))); + return implode("\n", array_filter($clauses)); } /** @@ -69,6 +82,7 @@ class QueryBuilder extends \yii\base\Object * The method will properly escape the column names, and bind the values to be inserted. * @param string $table the table that new rows will be inserted into. * @param array $columns the column data (name=>value) to be inserted into the table. + * @param array $params the parameters to be bound to the query. * @return integer number of rows affected by the execution. */ public function insert($table, $columns, &$params = array()) @@ -89,6 +103,9 @@ class QueryBuilder extends \yii\base\Object $count++; } } + if ($this->_query instanceof Query) { + $this->_query->addParams($params); + } return 'INSERT INTO ' . $this->schema->quoteTableName($table) . ' (' . implode(', ', $names) . ') VALUES (' @@ -100,12 +117,12 @@ class QueryBuilder extends \yii\base\Object * The method will properly escape the column names and bind the values to be updated. * @param string $table the table to be updated. * @param array $columns the column data (name=>value) to be updated. - * @param mixed $conditions the conditions that will be put in the WHERE part. Please - * refer to {@link where} on how to specify conditions. + * @param mixed $condition the condition that will be put in the WHERE part. Please + * refer to [[Query::where()]] on how to specify condition. * @param array $params the parameters to be bound to the query. * @return integer number of rows affected by the execution. */ - public function update($table, $columns, $conditions = '', &$params = array()) + public function update($table, $columns, $condition = '', &$params = array()) { $lines = array(); $count = 0; @@ -121,8 +138,11 @@ class QueryBuilder extends \yii\base\Object $count++; } } + if ($this->_query instanceof Query) { + $this->_query->addParams($params); + } $sql = 'UPDATE ' . $this->schema->quoteTableName($table) . ' SET ' . implode(', ', $lines); - if (($where = $this->buildCondition($conditions)) != '') { + if (($where = $this->buildCondition($condition)) != '') { $sql .= ' WHERE ' . $where; } @@ -132,14 +152,14 @@ class QueryBuilder extends \yii\base\Object /** * Creates and executes a DELETE SQL statement. * @param string $table the table where the data will be deleted from. - * @param mixed $conditions the conditions that will be put in the WHERE part. Please - * refer to {@link where} on how to specify conditions. + * @param mixed $condition the condition that will be put in the WHERE part. Please + * refer to [[Query::where()]] on how to specify condition. * @return integer number of rows affected by the execution. */ - public function delete($table, $conditions = '') + public function delete($table, $condition = '') { $sql = 'DELETE FROM ' . $this->schema->quoteTableName($table); - if (($where = $this->buildCondition($conditions)) != '') { + if (($where = $this->buildCondition($condition)) != '') { $sql .= ' WHERE ' . $where; } return $sql; @@ -495,8 +515,7 @@ class QueryBuilder extends \yii\base\Object } $joins[$i] = strtoupper($join[0]) . ' ' . $table; if (isset($join[2])) { // join condition - $condition = $this->buildCondition($join[2]); - $joins[$i] .= ' ON ' . $condition; + $joins[$i] .= ' ON ' . $this->buildCondition($join[2]); } } else { throw new Exception('The join clause may be specified as an array of at least two elements.'); @@ -598,46 +617,46 @@ class QueryBuilder extends \yii\base\Object return "UNION (\n" . implode("\n) UNION (\n", $unions) . "\n)"; } - protected function buildCondition($conditions) + protected function buildCondition($condition) { - if (!is_array($conditions)) { - return $conditions; - } elseif ($conditions === array()) { + if (!is_array($condition)) { + return $condition; + } elseif ($condition === array()) { return ''; } - $n = count($conditions); - $operator = strtoupper($conditions[0]); + $n = count($condition); + $operator = strtoupper($condition[0]); if ($operator === 'OR' || $operator === 'AND') { $parts = array(); for ($i = 1; $i < $n; ++$i) { - $condition = $this->buildCondition($conditions[$i]); - if ($condition !== '') { - $parts[] = '(' . $condition . ')'; + $part = $this->buildCondition($condition[$i]); + if ($part !== '') { + $parts[] = '(' . $part . ')'; } } return $parts === array() ? '' : implode(' ' . $operator . ' ', $parts); } - if (!isset($conditions[1], $conditions[2])) { + if (!isset($condition[1], $condition[2])) { throw new Exception("Operator $operator requires at least two operands."); } - $column = $conditions[1]; + $column = $condition[1]; if (strpos($column, '(') === false) { $column = $this->connection->quoteColumnName($column); } if ($operator === 'BETWEEN' || $operator === 'NOT BETWEEN') { - if (!isset($conditions[3])) { + if (!isset($condition[3])) { throw new Exception("Operator $operator requires three operands."); } - $value1 = is_string($conditions[2]) ? $this->connection->quoteValue($conditions[2]) : (string)$conditions[2]; - $value2 = is_string($conditions[3]) ? $this->connection->quoteValue($conditions[3]) : (string)$conditions[3]; + $value1 = is_string($condition[2]) ? $this->connection->quoteValue($condition[2]) : (string)$condition[2]; + $value2 = is_string($condition[3]) ? $this->connection->quoteValue($condition[3]) : (string)$condition[3]; return "$column $operator $value1 AND $value2"; } - $values = $conditions[2]; + $values = $condition[2]; if (!is_array($values)) { $values = array($values); } diff --git a/tests/unit/framework/db/dao/QueryTest.php b/tests/unit/framework/db/dao/QueryTest.php new file mode 100644 index 0000000..05b9f86 --- /dev/null +++ b/tests/unit/framework/db/dao/QueryTest.php @@ -0,0 +1,215 @@ +select(); + $this->assertEquals('*', $query->select); + $this->assertFalse($query->distinct); + $this->assertEquals(null, $query->selectOption); + + $query = new Query; + $query->select('id, name', true, 'something'); + $this->assertEquals('id, name', $query->select); + $this->assertTrue($query->distinct); + $this->assertEquals('something', $query->selectOption); + } + + function testFrom() + { + $query = new Query; + $query->from('tbl_user'); + $this->assertEquals('tbl_user', $query->from); + } + + function testWhere() + { + $query = new Query; + $query->where('id = :id', array(':id' => 1)); + $this->assertEquals('id = :id', $query->where); + $this->assertEquals(array(':id' => 1), $query->params); + + $query->andWhere('name = :name', array(':name' => 'something')); + $this->assertEquals(array('and', 'id = :id', 'name = :name'), $query->where); + $this->assertEquals(array(':id' => 1, ':name' => 'something'), $query->params); + + $query->orWhere('age = :age', array(':age' => '30')); + $this->assertEquals(array('or', array('and', 'id = :id', 'name = :name'), 'age = :age'), $query->where); + $this->assertEquals(array(':id' => 1, ':name' => 'something', ':age' => '30'), $query->params); + } + + function testJoin() + { + + } + + function testGroupBy() + { + $query = new Query; + $query->groupBy('team'); + $this->assertEquals('team', $query->groupBy); + + $query->addGroupBy('company'); + $this->assertEquals(array('team', 'company'), $query->groupBy); + + $query->addGroupBy('age'); + $this->assertEquals(array('team', 'company', 'age'), $query->groupBy); + } + + function testHaving() + { + $query = new Query; + $query->having('id = :id', array(':id' => 1)); + $this->assertEquals('id = :id', $query->having); + $this->assertEquals(array(':id' => 1), $query->params); + + $query->andHaving('name = :name', array(':name' => 'something')); + $this->assertEquals(array('and', 'id = :id', 'name = :name'), $query->having); + $this->assertEquals(array(':id' => 1, ':name' => 'something'), $query->params); + + $query->orHaving('age = :age', array(':age' => '30')); + $this->assertEquals(array('or', array('and', 'id = :id', 'name = :name'), 'age = :age'), $query->having); + $this->assertEquals(array(':id' => 1, ':name' => 'something', ':age' => '30'), $query->params); + } + + function testOrderBy() + { + $query = new Query; + $query->orderBy('team'); + $this->assertEquals('team', $query->orderBy); + + $query->addOrderBy('company'); + $this->assertEquals(array('team', 'company'), $query->orderBy); + + $query->addOrderBy('age'); + $this->assertEquals(array('team', 'company', 'age'), $query->orderBy); + } + + function testLimitOffset() + { + $query = new Query; + $query->limit(10)->offset(5); + $this->assertEquals(10, $query->limit); + $this->assertEquals(5, $query->offset); + } + + function testUnion() + { + + } + + function testInsert() + { + + } + + function testUpdate() + { + + } + + function testDelete() + { + + } + + function testCreateTable() + { + + } + + function testRenameTable() + { + + } + + function testDropTable() + { + + } + + function testTruncateTable() + { + + } + + function testAddColumn() + { + + } + + function testDropColumn() + { + + } + + function testRenameColumn() + { + + } + + function testAlterColumn() + { + + } + + function testAddForeignKey() + { + + } + + function testDropForeignKey() + { + + } + + function testCreateIndex() + { + + } + + function testDropIndex() + { + + } + + function testParams() + { + + } + + function testGetSql() + { + + } + + function testCreateCommand() + { + + } + + function testReset() + { + + } + + function testToArray() + { + + } + + function testMergeWith() + { + + } +} \ No newline at end of file