diff -u b/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php --- b/core/lib/Drupal/Core/Cache/DatabaseBackend.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php @@ -444,7 +444,7 @@ /** * Defines the schema for the {cache_*} bin and {cachetags} tables. */ - public function getSchema($table_name = NULL) { + public function getSchema($table_name = '') { $schema[$this->bin] = array( 'description' => 'Storage for the cache API.', 'fields' => array( diff -u b/core/lib/Drupal/Core/Config/DatabaseStorage.php b/core/lib/Drupal/Core/Config/DatabaseStorage.php --- b/core/lib/Drupal/Core/Config/DatabaseStorage.php +++ b/core/lib/Drupal/Core/Config/DatabaseStorage.php @@ -71,8 +71,9 @@ * Implements Drupal\Core\Config\StorageInterface::exists(). */ public function exists($name) { - $options = $this->options; - $options['on_table_missing'] = FALSE; + $options = array( + 'create_missing_table' => FALSE, + ) + $this->options; try { return (bool) $this->connection->queryRange('SELECT 1 FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name = :name', 0, 1, array( ':collection' => $this->collection, @@ -90,40 +91,16 @@ * {@inheritdoc} */ public function read($name) { - $options = $this->options; - $options['on_table_missing'] = FALSE; - $data = FALSE; - try { - $raw = $this->connection->query('SELECT data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name = :name', array(':collection' => $this->collection, ':name' => $name), $options)->fetchField(); - if ($raw !== FALSE) { - $data = $this->decode($raw); - } - } - catch (\Exception $e) { - // If we attempt a read without actually having the database or the table - // available, just return FALSE so the caller can handle it. - } - return $data; + $raw = $this->connection->query('SELECT data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name = :name', array(':collection' => $this->collection, ':name' => $name), $this->options)->fetchField(); + return $raw !== FALSE ? $this->decode($raw) : FALSE; } /** * {@inheritdoc} */ public function readMultiple(array $names) { - $options = $this->options; - $options['on_table_missing'] = FALSE; - $list = array(); - try { - $list = $this->connection->query('SELECT name, data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name IN (:names)', array(':collection' => $this->collection, ':names' => $names), $options)->fetchAllKeyed(); - foreach ($list as &$data) { - $data = $this->decode($data); - } - } - catch (\Exception $e) { - // If we attempt a read without actually having the database or the table - // available, just return an empty array so the caller can handle it. - } - return $list; + $list = $this->connection->query('SELECT name, data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name IN (:names)', array(':collection' => $this->collection, ':names' => $names), $this->options)->fetchAllKeyed(); + return array_map([$this, 'decode'], $list); } /** @@ -141,7 +118,7 @@ /** * Defines the schema for the configuration table. */ - public function getSchema($table_name = NULL) { + public function getSchema($table_name = '') { $schema = array( 'description' => 'The base table for configuration data.', 'fields' => array( @@ -179,7 +156,10 @@ * @todo Ignore replica targets for data manipulation operations. */ public function delete($name) { - $options = array('return' => Database::RETURN_AFFECTED) + $this->options; + $options = array( + 'return' => Database::RETURN_AFFECTED, + 'create_missing_table' => FALSE, + ) + $this->options; return (bool) $this->connection->delete($this->table, $options) ->condition('collection', $this->collection) ->condition('name', $name) @@ -240,7 +220,10 @@ */ public function deleteAll($prefix = '') { try { - $options = array('return' => Database::RETURN_AFFECTED) + $this->options; + $options = array( + 'return' => Database::RETURN_AFFECTED, + 'create_missing_table' => FALSE, + ) + $this->options; return (bool) $this->connection->delete($this->table, $options) ->condition('name', $prefix . '%', 'LIKE') ->condition('collection', $this->collection) diff -u b/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php --- b/core/lib/Drupal/Core/Database/Connection.php +++ b/core/lib/Drupal/Core/Database/Connection.php @@ -218,7 +218,7 @@ * - Database::RETURN_NULL: Do not return anything, as there is no * meaningful value to return. That is the case for INSERT queries on * tables that do not contain a serial column. - * - on_table_missing: When a string, it is a table name. If the query + * - create_missing_table: When a string, it is a table name. If the query * fails with a table missing error then this table is created and if * the creation succeeded then the query is re-run. When TRUE and * return is set to Database::RETURN_STATEMENT and the query is failing @@ -238,7 +238,7 @@ 'target' => 'default', 'fetch' => \PDO::FETCH_OBJ, 'return' => Database::RETURN_STATEMENT, - 'on_table_missing' => TRUE, + 'create_missing_table' => TRUE, 'throw_exception' => TRUE, ); } @@ -562,24 +562,30 @@ } } catch (\PDOException $e) { - if ($this->isTableMissingException($e) && !empty($options['on_table_missing'])) { + if ($this->isTableMissingException($e) && !empty($options['create_missing_table'])) { if ($options['return'] == Database::RETURN_STATEMENT) { + // This is a SELECT on a missing table. Creating the table and + // re-runing the query will result in an empty result so return that + // early instead. return new StatementEmpty(); } - if (is_string($options['on_table_missing'])) { - $table = $options['on_table_missing']; + if (is_string($options['create_missing_table'])) { + $table = $options['create_missing_table']; foreach (debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT) as $bt) { - if (!empty($bt['object']) && $bt['object'] instanceof SchemaProviderInterface && ($schema = call_user_func([$bt['object'], 'getSchema'], $table))) { + if (isset($bt['object']) && $bt['object'] instanceof SchemaProviderInterface && ($schema = $bt['object']->getSchema($table))) { if ($this->schema()->ensureTableExists($table, $schema)) { // Theoretically the table must exist at this point so this // should not matter but still, databases. This makes sure // no infinite recursion happens. - $options['on_table_missing'] = FALSE; + $options['create_missing_table'] = FALSE; return $this->query($query, $args, $options); } } } } + elseif ($options['create_missing_table'] === TRUE) { + return NULL; + } } if ($options['throw_exception']) { // Wrap the exception in another exception, because PHP does not allow diff -u b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php --- b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php @@ -513,7 +513,7 @@ // @todo This override should be removed once we require a version of MySQL // that has that bug fixed. try { - $options['on_table_missing'] = FALSE; + $options['create_missing_table'] = FALSE; $this->connection->queryRange("SELECT 1 FROM {" . $table . "}", 0, 1, [], $options); return TRUE; } diff -u b/core/lib/Drupal/Core/Database/Query/Delete.php b/core/lib/Drupal/Core/Database/Query/Delete.php --- b/core/lib/Drupal/Core/Database/Query/Delete.php +++ b/core/lib/Drupal/Core/Database/Query/Delete.php @@ -45,9 +45,6 @@ */ public function __construct(Connection $connection, $table, array $options = array()) { $options['return'] = Database::RETURN_AFFECTED; - if (!isset($options['on_table_missing'])) { - $options['on_table_missing'] = FALSE; - } parent::__construct($connection, $options); $this->table = $table; @@ -142,18 +139,7 @@ $this->condition->compile($this->connection, $this); $values = $this->condition->arguments(); } - try { - return $this->connection->query((string) $this, $values, $this->queryOptions); - } - catch (\Exception $e) { - if ($this->connection->isTableMissingException($e)) { - // There are no rows to delete because the table doesn't exist so the - // desired results are achieved. - return 0; - } - throw $e; - } - + return $this->connection->query((string) $this, $values, $this->queryOptions); } /** diff -u b/core/lib/Drupal/Core/Database/Query/Insert.php b/core/lib/Drupal/Core/Database/Query/Insert.php --- b/core/lib/Drupal/Core/Database/Query/Insert.php +++ b/core/lib/Drupal/Core/Database/Query/Insert.php @@ -74,7 +74,7 @@ public function __construct($connection, $table, array $options = array()) { $options += array( 'return' => Database::RETURN_INSERT_ID, - 'on_table_missing' => $table, + 'create_missing_table' => $table, ); parent::__construct($connection, $options); $this->table = $table; diff -u b/core/lib/Drupal/Core/Database/Query/Merge.php b/core/lib/Drupal/Core/Database/Query/Merge.php --- b/core/lib/Drupal/Core/Database/Query/Merge.php +++ b/core/lib/Drupal/Core/Database/Query/Merge.php @@ -135,8 +135,8 @@ */ public function __construct(Connection $connection, $table, array $options = array()) { $options['return'] = Database::RETURN_AFFECTED; - if (!isset($options['on_table_missing'])) { - $options['on_table_missing'] = $table; + if (!isset($options['create_missing_table'])) { + $options['create_missing_table'] = $table; } parent::__construct($connection, $options); $this->table = $table; diff -u b/core/lib/Drupal/Core/Database/Query/Select.php b/core/lib/Drupal/Core/Database/Query/Select.php --- b/core/lib/Drupal/Core/Database/Query/Select.php +++ b/core/lib/Drupal/Core/Database/Query/Select.php @@ -137,9 +137,6 @@ */ public function __construct($table, $alias = NULL, Connection $connection, $options = array()) { $options['return'] = Database::RETURN_STATEMENT; - if (!isset($options['on_table_exception'])) { - $options['on_table_exception'] = TRUE; - } parent::__construct($connection, $options); $conjunction = isset($options['conjunction']) ? $options['conjunction'] : 'AND'; $this->where = new Condition($conjunction); @@ -685,6 +682,9 @@ * {@inheritdoc} */ public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = array()) { + if (isset($type)) { + $this->queryOptions['create_missing_table'] = FALSE; + } if (empty($alias)) { if ($table instanceof SelectInterface) { $alias = 'subquery'; diff -u b/core/lib/Drupal/Core/Database/Query/Truncate.php b/core/lib/Drupal/Core/Database/Query/Truncate.php --- b/core/lib/Drupal/Core/Database/Query/Truncate.php +++ b/core/lib/Drupal/Core/Database/Query/Truncate.php @@ -35,9 +35,6 @@ */ public function __construct(Connection $connection, $table, array $options = array()) { $options['return'] = Database::RETURN_AFFECTED; - if (!isset($options['on_table_missing'])) { - $options['on_table_missing'] = FALSE; - } parent::__construct($connection, $options); $this->table = $table; } reverted: --- b/core/lib/Drupal/Core/Database/Query/Update.php +++ a/core/lib/Drupal/Core/Database/Query/Update.php @@ -74,9 +74,6 @@ */ public function __construct(Connection $connection, $table, array $options = array()) { $options['return'] = Database::RETURN_AFFECTED; - if (!isset($options['on_table_missing'])) { - $options['on_table_missing'] = FALSE; - } parent::__construct($connection, $options); $this->table = $table; @@ -202,30 +199,13 @@ } /** - * {@inheritdoc} - */ - public function execute() { - try { - return $this->doExecute(); - } - catch (\Exception $e) { - if ($this->connection->isTableMissingException($e)) { - // Every row (in the non-existing table, that is, so no rows) are - // updated. - return 0; - } - throw $e; - } - } - - /** * Executes the UPDATE query. * * @return * The number of rows matched by the update query. This includes rows that * actually didn't have to be updated because the values didn't change. */ + public function execute() { - protected function doExecute() { // Expressions take priority over literal fields, so we process those first // and remove any literal fields that conflict. $fields = $this->fields; diff -u b/core/lib/Drupal/Core/Database/Schema.php b/core/lib/Drupal/Core/Database/Schema.php --- b/core/lib/Drupal/Core/Database/Schema.php +++ b/core/lib/Drupal/Core/Database/Schema.php @@ -768,7 +768,2 @@ } - - protected function query($query, array $args = array(), $options = array()) { - $options['on_table_missing'] = FALSE; - return $this->connection->query($query, $args, $options); - } } diff -u b/core/lib/Drupal/Core/Database/SchemaProviderInterface.php b/core/lib/Drupal/Core/Database/SchemaProviderInterface.php --- b/core/lib/Drupal/Core/Database/SchemaProviderInterface.php +++ b/core/lib/Drupal/Core/Database/SchemaProviderInterface.php @@ -10,22 +10,24 @@ * This interface is a helper for queries creating tables on demand. * - * Passing objects implementing this interface to - * Drupal\Core\Database\Query::executeEnsuringTable() and - * Drupal\Core\Database\SelectInterface::executeEnsuringTable() allows these - * methods to create the necessary table if it is missing and re-run the query. - * For example, during testing this avoids creating unnecessary tables ahead of - * the time. + * An INSERT or MERGE query will automatically find if the caller implements + * this interface and create the table on demand. + * */ interface SchemaProviderInterface { /** * A schema API array. * + * @param string $table_name + * The name of the table being created. Typically it's safe to ignore this + * unless the class implementing this interface works with multiple tables. + * * @return array * A schema API array * * @see hook_schema() + * @see \Drupal\Core\Cache\DatabaseBackend */ - public function getSchema($table_name = NULL); + public function getSchema($table_name = ''); } diff -u b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php --- b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php @@ -1004,7 +1004,7 @@ if ($menu_name) { $query->condition('menu_name', $menu_name); } - return $query->countQuery()->executeEnsuringTable($this)->fetchField(); + return $query->countQuery()->execute()->fetchField(); } /** @@ -1136,7 +1136,7 @@ * @return array * The schema API definition for the SQL storage table. */ - public function getSchema($table_name = NULL) { + public function getSchema($table_name = '') { $schema = array( 'description' => 'Contains the menu tree hierarchy.', 'fields' => array( diff -u b/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php b/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php --- b/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php +++ b/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php @@ -48,7 +48,8 @@ $bin->set('test', 'value', Cache::PERMANENT, $tags); $this->assertTrue($bin->get('test'), 'Cache item was set in bin.'); } - + // Just setting cache data does not create the cachetags table, that only + // happens when tags are invalidated. $this->assertFalse(Database::getConnection()->schema()->tableExists('cachetags')); Cache::invalidateTags(array('test_tag:2')); diff -u b/core/modules/system/src/Tests/Database/SelectTest.php b/core/modules/system/src/Tests/Database/SelectTest.php --- b/core/modules/system/src/Tests/Database/SelectTest.php +++ b/core/modules/system/src/Tests/Database/SelectTest.php @@ -477,7 +477,7 @@ try { // This query will fail because the table does not exist. - db_select('some_table_that_doesnt_exist', 't', ['on_table_missing' => FALSE]) + db_select('some_table_that_doesnt_exist', 't', ['create_missing_table' => FALSE]) ->fields('t') ->countQuery() ->execute(); @@ -523,7 +523,7 @@ * * @return array */ - public function getSchema($table_name = NULL) { + public function getSchema($table_name = '') { return drupal_get_schema_unprocessed('database_test', 'test'); } } only in patch2: unchanged: --- a/core/lib/Drupal/Core/Database/StatementEmpty.php +++ b/core/lib/Drupal/Core/Database/StatementEmpty.php @@ -52,7 +52,7 @@ public function fetch($mode = NULL, $cursor_orientation = NULL, $cursor_offset = } public function fetchField($index = 0) { - return NULL; + return FALSE; } public function fetchObject() { only in patch2: unchanged: --- a/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php +++ b/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php @@ -69,7 +69,7 @@ public function triggerException() { */ public function triggerPDOException() { define('SIMPLETEST_COLLECT_ERRORS', FALSE); - $this->database->query('SELECT * FROM bananas_are_awesome'); + $this->database->query('SELECT * FROM bananas_are_awesome', [], ['create_missing_table' => FALSE]); } }