summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--includes/database/database.inc53
-rw-r--r--modules/simpletest/tests/database_test.test26
2 files changed, 77 insertions, 2 deletions
diff --git a/includes/database/database.inc b/includes/database/database.inc
index 411d2cfd7..900a518c7 100644
--- a/includes/database/database.inc
+++ b/includes/database/database.inc
@@ -349,10 +349,14 @@ abstract class DatabaseConnection extends PDO {
* @param $query
* The query string as SQL, with curly-braces surrounding the
* table names.
+ * @param $query
+ * Whether or not to cache the prepared statement for later reuse in this
+ * same request. Usually we want to, but queries that require preprocessing
+ * cannot be safely cached.
* @return
* A PDO prepared statement ready for its execute() method.
*/
- protected function prepareQuery($query) {
+ protected function prepareQuery($query, $cache = TRUE) {
$query = self::prefixTables($query);
if (empty($this->preparedStatements[$query])) {
// Call PDO::prepare.
@@ -473,7 +477,8 @@ abstract class DatabaseConnection extends PDO {
$stmt->execute(NULL, $options);
}
else {
- $stmt = $this->prepareQuery($query);
+ $modified = $this->expandArguments($query, $args);
+ $stmt = $this->prepareQuery($query, !$modified);
$stmt->execute($args, $options);
}
@@ -508,6 +513,50 @@ abstract class DatabaseConnection extends PDO {
}
/**
+ * Expand out shorthand placeholders.
+ *
+ * Drupal supports an alternate syntax for doing arrays of values. We therefore
+ * need to expand them out into a full, executable query string.
+ *
+ * @param $query
+ * The query string to modify.
+ * @param $args
+ * The arguments for the query.
+ * @return
+ * TRUE if the query was modified, FALSE otherwise.
+ */
+ protected function expandArguments(&$query, &$args) {
+ $modified = FALSE;
+
+ foreach ($args as $key => $data) {
+ // is_array() is slower than checking a string value, so do that first.
+ if (is_array($data)) {
+ $new_keys = array();
+ $base = $key;
+ $base[0] = ':';
+ foreach ($data as $i => $value) {
+ $candidate_placeholder = $base . '_' . $i;
+ while (isset($args[$candidate_placeholder])) {
+ $candidate_placeholder .= mt_rand();
+ }
+ $new_keys[$candidate_placeholder] = $value;
+ }
+
+ // Update the query with the new placeholders.
+ $query = str_replace($key, implode(', ', $new_keys), $query);
+
+ // Update the args array with the new placeholders.
+ unset($args[$key]);
+ $args += $new_keys;
+
+ $modified = TRUE;
+ }
+ }
+
+ return $modified;
+ }
+
+ /**
* Prepare and return a SELECT query object with the specified ID.
*
* @see SelectQuery
diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test
index 07efca610..15802ed1b 100644
--- a/modules/simpletest/tests/database_test.test
+++ b/modules/simpletest/tests/database_test.test
@@ -2067,3 +2067,29 @@ class DatabaseInvalidDataTestCase extends DatabaseTestCase {
}
}
}
+
+/**
+ * Drupal-specific SQL syntax tests.
+ */
+class DatabaseQueryTestCase extends DatabaseTestCase {
+ function getInfo() {
+ return array(
+ 'name' => t('Custom query syntax tests'),
+ 'description' => t('Test Drupal\'s extended prepared statement syntax..'),
+ 'group' => t('Database'),
+ );
+ }
+
+ function setUp() {
+ parent::setUp('database_test');
+ }
+
+ /**
+ * Confirm that temporary tables work and are limited to one request.
+ */
+ function testArraySubstitution() {
+ $names = db_query("SELECT name FROM {test} WHERE age IN (@ages) ORDER BY age", array('@ages' => array(25, 26, 27)))->fetchAll();
+
+ $this->assertEqual(count($names), 3, t('Correct number of names returned'));
+ }
+}