diff --git a/includes/database/mysql/query.inc b/includes/database/mysql/query.inc index 843e22dfd2d29a27804bf2a69c4ee71d056af261..63c0fe8e72200794aa79a15f3b8120b51b19ef59 100644 --- a/includes/database/mysql/query.inc +++ b/includes/database/mysql/query.inc @@ -43,6 +43,8 @@ public function execute() { } public function __toString() { + // Create a comments string to prepend to the query. + $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : ''; // Default fields are always placed first for consistency. $insert_fields = array_merge($this->defaultFields, $this->insertFields); @@ -50,10 +52,10 @@ public function __toString() { // If we're selecting from a SelectQuery, finish building the query and // pass it back, as any remaining options are irrelevant. if (!empty($this->fromQuery)) { - return "INSERT INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; + return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; } - $query = "INSERT INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES '; + $query = $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES '; $max_placeholder = 0; $values = array(); @@ -143,6 +145,8 @@ public function execute() { public function __toString() { + // Create a comments string to prepend to the query. + $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : ''; // Set defaults. if ($this->updateFields) { @@ -164,7 +168,7 @@ public function __toString() { $insert_fields = $this->insertFields + $this->keyFields; - $query = "INSERT INTO {" . $this->table . '} (' . implode(', ', array_keys($insert_fields)) . ') VALUES '; + $query = $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', array_keys($insert_fields)) . ') VALUES '; $max_placeholder = 0; $values = array(); diff --git a/includes/database/pgsql/query.inc b/includes/database/pgsql/query.inc index 0420f4aad2764cac8774f109091a200368741f4d..cb1b94ba26f6821b924b1a0e6106e82a7bf65a6f 100644 --- a/includes/database/pgsql/query.inc +++ b/includes/database/pgsql/query.inc @@ -71,6 +71,8 @@ public function execute() { } public function __toString() { + // Create a comments string to prepend to the query. + $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : ''; // Default fields are always placed first for consistency. $insert_fields = array_merge($this->defaultFields, $this->insertFields); @@ -78,10 +80,10 @@ public function __toString() { // If we're selecting from a SelectQuery, finish building the query and // pass it back, as any remaining options are irrelevant. if (!empty($this->fromQuery)) { - return "INSERT INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; + return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; } - $query = "INSERT INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES '; + $query = $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES '; $max_placeholder = 0; $values = array(); diff --git a/includes/database/query.inc b/includes/database/query.inc index 2e775bea5fa17a2609ccd8f3a30f2186e60e22f6..3e69872359dccdcd4a78e893da698b232fd21176 100644 --- a/includes/database/query.inc +++ b/includes/database/query.inc @@ -240,6 +240,13 @@ abstract class Query implements QueryPlaceholderInterface { */ protected $nextPlaceholder = 0; + /** + * An array of comments that can be prepended to a query. + * + * @var array + */ + protected $comments = array(); + public function __construct(DatabaseConnection $connection, $options) { $this->connection = $connection; $this->queryOptions = $options; @@ -263,6 +270,44 @@ abstract public function __toString(); public function nextPlaceholder() { return $this->nextPlaceholder++; } + + /** + * Adds a comment to the query. + * + * By adding a comment to a query, you can more easily find it in your + * query log or the list of active queries on an sql server. This allows + * for easier debugging and allows you to more easily find where a query + * with a performance problem is being generated. + * + * @param $comment + * The comment string to be inserted into the query. + * @return Query + * The called object. + */ + public function comment($comment) { + $this->comments[] = $comment; + return $this; + } + + /** + * Returns a reference to the comments array for the query. + * + * Because this method returns by reference, alter hooks may edit the comments + * array directly to make their changes. If just adding comments, however, the + * use of comment() is preferred. + * + * Note that this method must be called by reference as well: + * + * @code + * $comments =& $query->getComments(); + * @endcode + * + * @return + * A reference to the comments array structure. + */ + public function &getComments() { + return $this->comments; + } } /** @@ -468,11 +513,14 @@ public function execute() { public function __toString() { + // Create a comments string to prepend to the query. + $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : ''; + // Default fields are always placed first for consistency. $insert_fields = array_merge($this->defaultFields, $this->insertFields); if (!empty($this->fromQuery)) { - return "INSERT INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; + return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery; } // For simplicity, we will use the $placeholders array to inject @@ -482,7 +530,7 @@ public function __toString() { $placeholders = array_pad($placeholders, count($this->defaultFields), 'default'); $placeholders = array_pad($placeholders, count($this->insertFields), '?'); - return 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES (' . implode(', ', $placeholders) . ')'; + return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES (' . implode(', ', $placeholders) . ')'; } /** @@ -900,7 +948,11 @@ public function execute() { } public function __toString() { - $query = 'DELETE FROM {' . $this->connection->escapeTable($this->table) . '} '; + + // Create a comments string to prepend to the query. + $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : ''; + + $query = $comments . 'DELETE FROM {' . $this->connection->escapeTable($this->table) . '} '; if (count($this->condition)) { @@ -940,7 +992,10 @@ public function execute() { } public function __toString() { - return 'TRUNCATE {' . $this->connection->escapeTable($this->table) . '} '; + // Create a comments string to prepend to the query. + $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : ''; + + return $comments . 'TRUNCATE {' . $this->connection->escapeTable($this->table) . '} '; } } @@ -1100,6 +1155,10 @@ public function execute() { } public function __toString() { + + // Create a comments string to prepend to the query. + $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : ''; + // Expressions take priority over literal fields, so we process those first // and remove any literal fields that conflict. $fields = $this->fields; @@ -1114,7 +1173,7 @@ public function __toString() { $update_fields[] = $field . '=:db_update_placeholder_' . ($max_placeholder++); } - $query = 'UPDATE {' . $this->connection->escapeTable($this->table) . '} SET ' . implode(', ', $update_fields); + $query = $comments . 'UPDATE {' . $this->connection->escapeTable($this->table) . '} SET ' . implode(', ', $update_fields); if (count($this->condition)) { $this->condition->compile($this->connection, $this); diff --git a/includes/database/select.inc b/includes/database/select.inc index ddb78c69de65feade8cb5e26e9485ae29bfa7416..5cb4c04f48e859f8739ef0f8394f3d79c1d1b730 100644 --- a/includes/database/select.inc +++ b/includes/database/select.inc @@ -1317,8 +1317,11 @@ public function countQuery() { public function __toString() { + // Create a comments string to prepend to the query. + $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : ''; + // SELECT - $query = 'SELECT '; + $query = $comments . 'SELECT '; if ($this->distinct) { $query .= 'DISTINCT '; } diff --git a/includes/database/sqlite/query.inc b/includes/database/sqlite/query.inc index de8f7f2126c6b9c2a10b1cf759fed26943569c90..08c929bf3faff64eb80278fdf756e747b274f13d 100644 --- a/includes/database/sqlite/query.inc +++ b/includes/database/sqlite/query.inc @@ -33,16 +33,19 @@ public function execute() { } public function __toString() { + // Create a comments string to prepend to the query. + $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : ''; + // Produce as many generic placeholders as necessary. $placeholders = array_fill(0, count($this->insertFields), '?'); // If we're selecting from a SelectQuery, finish building the query and // pass it back, as any remaining options are irrelevant. if (!empty($this->fromQuery)) { - return "INSERT INTO {" . $this->table . '} (' . implode(', ', $this->insertFields) . ') ' . $this->fromQuery; + return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $this->insertFields) . ') ' . $this->fromQuery; } - return 'INSERT INTO {' . $this->table . '} (' . implode(', ', $this->insertFields) . ') VALUES (' . implode(', ', $placeholders) . ')'; + return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $this->insertFields) . ') VALUES (' . implode(', ', $placeholders) . ')'; } } @@ -143,7 +146,10 @@ public function execute() { */ class TruncateQuery_sqlite extends TruncateQuery { public function __toString() { - return 'DELETE FROM {' . $this->connection->escapeTable($this->table) . '} '; + // Create a comments string to prepend to the query. + $comments = (!empty($this->comments)) ? '/* ' . implode('; ', $this->comments) . ' */ ' : ''; + + return $comments . 'DELETE FROM {' . $this->connection->escapeTable($this->table) . '} '; } } diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test index dd3f4185093843942b2137cc0d67b2542a67d16e..b64b5bd8113a53fd97a5fb754c267b7749fb261b 100644 --- a/modules/simpletest/tests/database_test.test +++ b/modules/simpletest/tests/database_test.test @@ -1015,7 +1015,6 @@ class DatabaseDeleteTruncateTestCase extends DatabaseTestCase { $this->assertEqual($num_records_before, $num_records_after + $num_deleted, t('Deletion adds up.')); } - /** * Confirm that we can truncate a whole table successfully. */ @@ -1274,6 +1273,27 @@ class DatabaseSelectTestCase extends DatabaseTestCase { $this->assertEqual($num_records, 4, t('Returned the correct number of rows.')); } + /** + * Test rudimentary SELECT statement with a COMMENT. + */ + function testSimpleComment() { + $query = db_select('test')->comment('Testing query comments'); + $name_field = $query->addField('test', 'name'); + $age_field = $query->addField('test', 'age', 'age'); + $result = $query->execute(); + + $num_records = 0; + foreach ($result as $record) { + $num_records++; + } + + $query = (string)$query; + $expected = "/* Testing query comments */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test"; + + $this->assertEqual($num_records, 4, t('Returned the correct number of rows.')); + $this->assertEqual($query, $expected, t('The flattened query contains the comment string.')); + } + /** * Test basic conditionals on SELECT statements. */