Chris@0: uniqueIdentifier = uniqid('', TRUE); Chris@0: $this->connection = $connection; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Implements the magic __clone function. Chris@0: */ Chris@0: public function __clone() { Chris@0: $this->uniqueIdentifier = uniqid('', TRUE); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function uniqueIdentifier() { Chris@0: return $this->uniqueIdentifier; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function nextPlaceholder() { Chris@0: return $this->placeholder++; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get information about the table name and schema from the prefix. Chris@0: * Chris@0: * @param Chris@0: * Name of table to look prefix up for. Defaults to 'default' because that's Chris@0: * default key for prefix. Chris@0: * @param $add_prefix Chris@0: * Boolean that indicates whether the given table name should be prefixed. Chris@0: * Chris@0: * @return Chris@0: * A keyed array with information about the schema, table name and prefix. Chris@0: */ Chris@0: protected function getPrefixInfo($table = 'default', $add_prefix = TRUE) { Chris@0: $info = [ Chris@0: 'schema' => $this->defaultSchema, Chris@0: 'prefix' => $this->connection->tablePrefix($table), Chris@0: ]; Chris@0: if ($add_prefix) { Chris@0: $table = $info['prefix'] . $table; Chris@0: } Chris@0: // If the prefix contains a period in it, then that means the prefix also Chris@0: // contains a schema reference in which case we will change the schema key Chris@0: // to the value before the period in the prefix. Everything after the dot Chris@0: // will be prefixed onto the front of the table. Chris@0: if (($pos = strpos($table, '.')) !== FALSE) { Chris@0: // Grab everything before the period. Chris@0: $info['schema'] = substr($table, 0, $pos); Chris@0: // Grab everything after the dot. Chris@0: $info['table'] = substr($table, ++$pos); Chris@0: } Chris@0: else { Chris@0: $info['table'] = $table; Chris@0: } Chris@0: return $info; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Create names for indexes, primary keys and constraints. Chris@0: * Chris@0: * This prevents using {} around non-table names like indexes and keys. Chris@0: */ Chris@0: public function prefixNonTable($table) { Chris@0: $args = func_get_args(); Chris@0: $info = $this->getPrefixInfo($table); Chris@0: $args[0] = $info['table']; Chris@0: return implode('_', $args); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Build a condition to match a table name against a standard information_schema. Chris@0: * Chris@0: * The information_schema is a SQL standard that provides information about the Chris@0: * database server and the databases, schemas, tables, columns and users within Chris@0: * it. This makes information_schema a useful tool to use across the drupal Chris@0: * database drivers and is used by a few different functions. The function below Chris@0: * describes the conditions to be meet when querying information_schema.tables Chris@0: * for drupal tables or information associated with drupal tables. Even though Chris@0: * this is the standard method, not all databases follow standards and so this Chris@0: * method should be overwritten by a database driver if the database provider Chris@0: * uses alternate methods. Because information_schema.tables is used in a few Chris@0: * different functions, a database driver will only need to override this function Chris@0: * to make all the others work. For example see Chris@0: * core/includes/databases/mysql/schema.inc. Chris@0: * Chris@0: * @param $table_name Chris@0: * The name of the table in question. Chris@0: * @param $operator Chris@0: * The operator to apply on the 'table' part of the condition. Chris@0: * @param $add_prefix Chris@0: * Boolean to indicate whether the table name needs to be prefixed. Chris@0: * Chris@0: * @return \Drupal\Core\Database\Query\Condition Chris@0: * A Condition object. Chris@0: */ Chris@0: protected function buildTableNameCondition($table_name, $operator = '=', $add_prefix = TRUE) { Chris@0: $info = $this->connection->getConnectionOptions(); Chris@0: Chris@0: // Retrieve the table name and schema Chris@0: $table_info = $this->getPrefixInfo($table_name, $add_prefix); Chris@0: Chris@0: $condition = new Condition('AND'); Chris@0: $condition->condition('table_catalog', $info['database']); Chris@0: $condition->condition('table_schema', $table_info['schema']); Chris@0: $condition->condition('table_name', $table_info['table'], $operator); Chris@0: return $condition; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Check if a table exists. Chris@0: * Chris@0: * @param $table Chris@0: * The name of the table in drupal (no prefixing). Chris@0: * Chris@0: * @return Chris@0: * TRUE if the given table exists, otherwise FALSE. Chris@0: */ Chris@0: public function tableExists($table) { Chris@0: $condition = $this->buildTableNameCondition($table); Chris@0: $condition->compile($this->connection, $this); Chris@0: // Normally, we would heartily discourage the use of string Chris@0: // concatenation for conditionals like this however, we Chris@0: // couldn't use db_select() here because it would prefix Chris@0: // information_schema.tables and the query would fail. Chris@0: // Don't use {} around information_schema.tables table. Chris@0: return (bool) $this->connection->query("SELECT 1 FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments())->fetchField(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Finds all tables that are like the specified base table name. Chris@0: * Chris@0: * @param string $table_expression Chris@0: * An SQL expression, for example "cache_%" (without the quotes). Chris@0: * Chris@0: * @return array Chris@0: * Both the keys and the values are the matching tables. Chris@0: */ Chris@0: public function findTables($table_expression) { Chris@0: // Load all the tables up front in order to take into account per-table Chris@0: // prefixes. The actual matching is done at the bottom of the method. Chris@0: $condition = $this->buildTableNameCondition('%', 'LIKE'); Chris@0: $condition->compile($this->connection, $this); Chris@0: Chris@0: $individually_prefixed_tables = $this->connection->getUnprefixedTablesMap(); Chris@0: $default_prefix = $this->connection->tablePrefix(); Chris@0: $default_prefix_length = strlen($default_prefix); Chris@0: $tables = []; Chris@0: // Normally, we would heartily discourage the use of string Chris@0: // concatenation for conditionals like this however, we Chris@0: // couldn't use db_select() here because it would prefix Chris@0: // information_schema.tables and the query would fail. Chris@0: // Don't use {} around information_schema.tables table. Chris@17: $results = $this->connection->query("SELECT table_name as table_name FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments()); Chris@0: foreach ($results as $table) { Chris@0: // Take into account tables that have an individual prefix. Chris@0: if (isset($individually_prefixed_tables[$table->table_name])) { Chris@0: $prefix_length = strlen($this->connection->tablePrefix($individually_prefixed_tables[$table->table_name])); Chris@0: } Chris@0: elseif ($default_prefix && substr($table->table_name, 0, $default_prefix_length) !== $default_prefix) { Chris@0: // This table name does not start the default prefix, which means that Chris@0: // it is not managed by Drupal so it should be excluded from the result. Chris@0: continue; Chris@0: } Chris@0: else { Chris@0: $prefix_length = $default_prefix_length; Chris@0: } Chris@0: Chris@0: // Remove the prefix from the returned tables. Chris@0: $unprefixed_table_name = substr($table->table_name, $prefix_length); Chris@0: Chris@0: // The pattern can match a table which is the same as the prefix. That Chris@0: // will become an empty string when we remove the prefix, which will Chris@0: // probably surprise the caller, besides not being a prefixed table. So Chris@0: // remove it. Chris@0: if (!empty($unprefixed_table_name)) { Chris@0: $tables[$unprefixed_table_name] = $unprefixed_table_name; Chris@0: } Chris@0: } Chris@0: Chris@0: // Convert the table expression from its SQL LIKE syntax to a regular Chris@0: // expression and escape the delimiter that will be used for matching. Chris@0: $table_expression = str_replace(['%', '_'], ['.*?', '.'], preg_quote($table_expression, '/')); Chris@0: $tables = preg_grep('/^' . $table_expression . '$/i', $tables); Chris@0: Chris@0: return $tables; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Check if a column exists in the given table. Chris@0: * Chris@0: * @param $table Chris@0: * The name of the table in drupal (no prefixing). Chris@18: * @param $column Chris@0: * The name of the column. Chris@0: * Chris@0: * @return Chris@0: * TRUE if the given column exists, otherwise FALSE. Chris@0: */ Chris@0: public function fieldExists($table, $column) { Chris@0: $condition = $this->buildTableNameCondition($table); Chris@0: $condition->condition('column_name', $column); Chris@0: $condition->compile($this->connection, $this); Chris@0: // Normally, we would heartily discourage the use of string Chris@0: // concatenation for conditionals like this however, we Chris@0: // couldn't use db_select() here because it would prefix Chris@0: // information_schema.tables and the query would fail. Chris@0: // Don't use {} around information_schema.columns table. Chris@0: return (bool) $this->connection->query("SELECT 1 FROM information_schema.columns WHERE " . (string) $condition, $condition->arguments())->fetchField(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns a mapping of Drupal schema field names to DB-native field types. Chris@0: * Chris@0: * Because different field types do not map 1:1 between databases, Drupal has Chris@0: * its own normalized field type names. This function returns a driver-specific Chris@0: * mapping table from Drupal names to the native names for each database. Chris@0: * Chris@0: * @return array Chris@0: * An array of Schema API field types to driver-specific field types. Chris@0: */ Chris@0: abstract public function getFieldTypeMap(); Chris@0: Chris@0: /** Chris@0: * Rename a table. Chris@0: * Chris@0: * @param $table Chris@0: * The table to be renamed. Chris@0: * @param $new_name Chris@0: * The new name for the table. Chris@0: * Chris@0: * @throws \Drupal\Core\Database\SchemaObjectDoesNotExistException Chris@0: * If the specified table doesn't exist. Chris@0: * @throws \Drupal\Core\Database\SchemaObjectExistsException Chris@0: * If a table with the specified new name already exists. Chris@0: */ Chris@0: abstract public function renameTable($table, $new_name); Chris@0: Chris@0: /** Chris@0: * Drop a table. Chris@0: * Chris@0: * @param $table Chris@0: * The table to be dropped. Chris@0: * Chris@0: * @return Chris@0: * TRUE if the table was successfully dropped, FALSE if there was no table Chris@0: * by that name to begin with. Chris@0: */ Chris@0: abstract public function dropTable($table); Chris@0: Chris@0: /** Chris@0: * Add a new field to a table. Chris@0: * Chris@0: * @param $table Chris@0: * Name of the table to be altered. Chris@0: * @param $field Chris@0: * Name of the field to be added. Chris@0: * @param $spec Chris@0: * The field specification array, as taken from a schema definition. Chris@0: * The specification may also contain the key 'initial', the newly Chris@0: * created field will be set to the value of the key in all rows. Chris@0: * This is most useful for creating NOT NULL columns with no default Chris@0: * value in existing tables. Chris@0: * Alternatively, the 'initial_form_field' key may be used, which will Chris@0: * auto-populate the new field with values from the specified field. Chris@0: * @param $keys_new Chris@0: * (optional) Keys and indexes specification to be created on the Chris@0: * table along with adding the field. The format is the same as a Chris@0: * table specification but without the 'fields' element. If you are Chris@0: * adding a type 'serial' field, you MUST specify at least one key Chris@18: * or index including it in this array. See ::changeField() for more Chris@0: * explanation why. Chris@0: * Chris@0: * @throws \Drupal\Core\Database\SchemaObjectDoesNotExistException Chris@0: * If the specified table doesn't exist. Chris@0: * @throws \Drupal\Core\Database\SchemaObjectExistsException Chris@0: * If the specified table already has a field by that name. Chris@0: */ Chris@0: abstract public function addField($table, $field, $spec, $keys_new = []); Chris@0: Chris@0: /** Chris@0: * Drop a field. Chris@0: * Chris@0: * @param $table Chris@0: * The table to be altered. Chris@0: * @param $field Chris@0: * The field to be dropped. Chris@0: * Chris@0: * @return Chris@0: * TRUE if the field was successfully dropped, FALSE if there was no field Chris@0: * by that name to begin with. Chris@0: */ Chris@0: abstract public function dropField($table, $field); Chris@0: Chris@0: /** Chris@0: * Set the default value for a field. Chris@0: * Chris@0: * @param $table Chris@0: * The table to be altered. Chris@0: * @param $field Chris@0: * The field to be altered. Chris@0: * @param $default Chris@0: * Default value to be set. NULL for 'default NULL'. Chris@0: * Chris@0: * @throws \Drupal\Core\Database\SchemaObjectDoesNotExistException Chris@0: * If the specified table or field doesn't exist. Chris@18: * Chris@18: * @deprecated as of Drupal 8.7.x, will be removed in Drupal 9.0.0. Instead, Chris@18: * call ::changeField() passing a full field specification. Chris@18: * Chris@18: * @see ::changeField() Chris@0: */ Chris@0: abstract public function fieldSetDefault($table, $field, $default); Chris@0: Chris@0: /** Chris@0: * Set a field to have no default value. Chris@0: * Chris@0: * @param $table Chris@0: * The table to be altered. Chris@0: * @param $field Chris@0: * The field to be altered. Chris@0: * Chris@0: * @throws \Drupal\Core\Database\SchemaObjectDoesNotExistException Chris@0: * If the specified table or field doesn't exist. Chris@18: * Chris@18: * @deprecated as of Drupal 8.7.x, will be removed in Drupal 9.0.0. Instead, Chris@18: * call ::changeField() passing a full field specification. Chris@18: * Chris@18: * @see ::changeField() Chris@0: */ Chris@0: abstract public function fieldSetNoDefault($table, $field); Chris@0: Chris@0: /** Chris@0: * Checks if an index exists in the given table. Chris@0: * Chris@0: * @param $table Chris@0: * The name of the table in drupal (no prefixing). Chris@0: * @param $name Chris@0: * The name of the index in drupal (no prefixing). Chris@0: * Chris@0: * @return Chris@0: * TRUE if the given index exists, otherwise FALSE. Chris@0: */ Chris@0: abstract public function indexExists($table, $name); Chris@0: Chris@0: /** Chris@0: * Add a primary key. Chris@0: * Chris@0: * @param $table Chris@0: * The table to be altered. Chris@0: * @param $fields Chris@0: * Fields for the primary key. Chris@0: * Chris@0: * @throws \Drupal\Core\Database\SchemaObjectDoesNotExistException Chris@0: * If the specified table doesn't exist. Chris@0: * @throws \Drupal\Core\Database\SchemaObjectExistsException Chris@0: * If the specified table already has a primary key. Chris@0: */ Chris@0: abstract public function addPrimaryKey($table, $fields); Chris@0: Chris@0: /** Chris@0: * Drop the primary key. Chris@0: * Chris@0: * @param $table Chris@0: * The table to be altered. Chris@0: * Chris@0: * @return Chris@0: * TRUE if the primary key was successfully dropped, FALSE if there was no Chris@0: * primary key on this table to begin with. Chris@0: */ Chris@0: abstract public function dropPrimaryKey($table); Chris@0: Chris@0: /** Chris@17: * Finds the primary key columns of a table, from the database. Chris@17: * Chris@17: * @param string $table Chris@17: * The name of the table. Chris@17: * Chris@17: * @return string[]|false Chris@17: * A simple array with the names of the columns composing the table's Chris@17: * primary key, or FALSE if the table does not exist. Chris@17: * Chris@17: * @throws \RuntimeException Chris@17: * If the driver does not override this method. Chris@17: */ Chris@17: protected function findPrimaryKeyColumns($table) { Chris@17: if (!$this->tableExists($table)) { Chris@17: return FALSE; Chris@17: } Chris@17: throw new \RuntimeException("The '" . $this->connection->driver() . "' database driver does not implement " . __METHOD__); Chris@17: } Chris@17: Chris@17: /** Chris@0: * Add a unique key. Chris@0: * Chris@0: * @param $table Chris@0: * The table to be altered. Chris@0: * @param $name Chris@0: * The name of the key. Chris@0: * @param $fields Chris@0: * An array of field names. Chris@0: * Chris@0: * @throws \Drupal\Core\Database\SchemaObjectDoesNotExistException Chris@0: * If the specified table doesn't exist. Chris@0: * @throws \Drupal\Core\Database\SchemaObjectExistsException Chris@0: * If the specified table already has a key by that name. Chris@0: */ Chris@0: abstract public function addUniqueKey($table, $name, $fields); Chris@0: Chris@0: /** Chris@0: * Drop a unique key. Chris@0: * Chris@0: * @param $table Chris@0: * The table to be altered. Chris@0: * @param $name Chris@0: * The name of the key. Chris@0: * Chris@0: * @return Chris@0: * TRUE if the key was successfully dropped, FALSE if there was no key by Chris@0: * that name to begin with. Chris@0: */ Chris@0: abstract public function dropUniqueKey($table, $name); Chris@0: Chris@0: /** Chris@0: * Add an index. Chris@0: * Chris@0: * @param $table Chris@0: * The table to be altered. Chris@0: * @param $name Chris@0: * The name of the index. Chris@0: * @param $fields Chris@0: * An array of field names or field information; if field information is Chris@0: * passed, it's an array whose first element is the field name and whose Chris@0: * second is the maximum length in the index. For example, the following Chris@0: * will use the full length of the `foo` field, but limit the `bar` field to Chris@0: * 4 characters: Chris@0: * @code Chris@0: * $fields = ['foo', ['bar', 4]]; Chris@0: * @endcode Chris@0: * @param array $spec Chris@0: * The table specification for the table to be altered. This is used in Chris@0: * order to be able to ensure that the index length is not too long. Chris@0: * This schema definition can usually be obtained through hook_schema(), or Chris@0: * in case the table was created by the Entity API, through the schema Chris@0: * handler listed in the entity class definition. For reference, see Chris@0: * SqlContentEntityStorageSchema::getDedicatedTableSchema() and Chris@0: * SqlContentEntityStorageSchema::getSharedTableFieldSchema(). Chris@0: * Chris@0: * In order to prevent human error, it is recommended to pass in the Chris@0: * complete table specification. However, in the edge case of the complete Chris@0: * table specification not being available, we can pass in a partial table Chris@0: * definition containing only the fields that apply to the index: Chris@0: * @code Chris@0: * $spec = [ Chris@0: * // Example partial specification for a table: Chris@0: * 'fields' => [ Chris@0: * 'example_field' => [ Chris@0: * 'description' => 'An example field', Chris@0: * 'type' => 'varchar', Chris@0: * 'length' => 32, Chris@0: * 'not null' => TRUE, Chris@0: * 'default' => '', Chris@0: * ], Chris@0: * ], Chris@0: * 'indexes' => [ Chris@0: * 'table_example_field' => ['example_field'], Chris@0: * ], Chris@0: * ]; Chris@0: * @endcode Chris@0: * Note that the above is a partial table definition and that we would Chris@0: * usually pass a complete table definition as obtained through Chris@0: * hook_schema() instead. Chris@0: * Chris@0: * @see schemaapi Chris@0: * @see hook_schema() Chris@0: * Chris@0: * @throws \Drupal\Core\Database\SchemaObjectDoesNotExistException Chris@0: * If the specified table doesn't exist. Chris@0: * @throws \Drupal\Core\Database\SchemaObjectExistsException Chris@0: * If the specified table already has an index by that name. Chris@0: * Chris@0: * @todo remove the $spec argument whenever schema introspection is added. Chris@0: */ Chris@0: abstract public function addIndex($table, $name, $fields, array $spec); Chris@0: Chris@0: /** Chris@0: * Drop an index. Chris@0: * Chris@0: * @param $table Chris@0: * The table to be altered. Chris@0: * @param $name Chris@0: * The name of the index. Chris@0: * Chris@0: * @return Chris@0: * TRUE if the index was successfully dropped, FALSE if there was no index Chris@0: * by that name to begin with. Chris@0: */ Chris@0: abstract public function dropIndex($table, $name); Chris@0: Chris@0: /** Chris@18: * Finds the columns for the primary key, unique keys and indexes of a table. Chris@18: * Chris@18: * @param string $table Chris@18: * The name of the table. Chris@18: * Chris@18: * @return array Chris@18: * A schema array with the following keys: 'primary key', 'unique keys' and Chris@18: * 'indexes', and values as arrays of database columns. Chris@18: * Chris@18: * @throws \Drupal\Core\Database\SchemaObjectDoesNotExistException Chris@18: * If the specified table doesn't exist. Chris@18: * @throws \RuntimeException Chris@18: * If the driver does not implement this method. Chris@18: */ Chris@18: protected function introspectIndexSchema($table) { Chris@18: if (!$this->tableExists($table)) { Chris@18: throw new SchemaObjectDoesNotExistException("The table $table doesn't exist."); Chris@18: } Chris@18: throw new \RuntimeException("The '{$this->connection->driver()}' database driver does not implement " . __METHOD__); Chris@18: } Chris@18: Chris@18: /** Chris@0: * Change a field definition. Chris@0: * Chris@0: * IMPORTANT NOTE: To maintain database portability, you have to explicitly Chris@0: * recreate all indices and primary keys that are using the changed field. Chris@0: * Chris@0: * That means that you have to drop all affected keys and indexes with Chris@18: * Schema::dropPrimaryKey(), Schema::dropUniqueKey(), or Schema::dropIndex() Chris@18: * before calling ::changeField(). Chris@0: * To recreate the keys and indices, pass the key definitions as the Chris@18: * optional $keys_new argument directly to ::changeField(). Chris@0: * Chris@0: * For example, suppose you have: Chris@0: * @code Chris@0: * $schema['foo'] = array( Chris@0: * 'fields' => array( Chris@0: * 'bar' => array('type' => 'int', 'not null' => TRUE) Chris@0: * ), Chris@0: * 'primary key' => array('bar') Chris@0: * ); Chris@0: * @endcode Chris@0: * and you want to change foo.bar to be type serial, leaving it as the Chris@0: * primary key. The correct sequence is: Chris@0: * @code Chris@18: * $injected_database->schema()->dropPrimaryKey('foo'); Chris@18: * $injected_database->schema()->changeField('foo', 'bar', 'bar', Chris@0: * array('type' => 'serial', 'not null' => TRUE), Chris@0: * array('primary key' => array('bar'))); Chris@0: * @endcode Chris@0: * Chris@0: * The reasons for this are due to the different database engines: Chris@0: * Chris@0: * On PostgreSQL, changing a field definition involves adding a new field Chris@0: * and dropping an old one which* causes any indices, primary keys and Chris@0: * sequences (from serial-type fields) that use the changed field to be dropped. Chris@0: * Chris@0: * On MySQL, all type 'serial' fields must be part of at least one key Chris@0: * or index as soon as they are created. You cannot use Chris@18: * Schema::addPrimaryKey, Schema::addUniqueKey(), or Schema::addIndex() Chris@18: * for this purpose because the ALTER TABLE command will fail to add Chris@18: * the column without a key or index specification. Chris@18: * The solution is to use the optional $keys_new argument to create the key Chris@18: * or index at the same time as field. Chris@0: * Chris@18: * You could use Schema::addPrimaryKey, Schema::addUniqueKey(), or Chris@18: * Schema::addIndex() in all cases unless you are converting a field to Chris@18: * be type serial. You can use the $keys_new argument in all cases. Chris@0: * Chris@0: * @param $table Chris@0: * Name of the table. Chris@0: * @param $field Chris@0: * Name of the field to change. Chris@0: * @param $field_new Chris@0: * New name for the field (set to the same as $field if you don't want to change the name). Chris@0: * @param $spec Chris@0: * The field specification for the new field. Chris@0: * @param $keys_new Chris@0: * (optional) Keys and indexes specification to be created on the Chris@0: * table along with changing the field. The format is the same as a Chris@0: * table specification but without the 'fields' element. Chris@0: * Chris@0: * @throws \Drupal\Core\Database\SchemaObjectDoesNotExistException Chris@0: * If the specified table or source field doesn't exist. Chris@0: * @throws \Drupal\Core\Database\SchemaObjectExistsException Chris@0: * If the specified destination field already exists. Chris@0: */ Chris@0: abstract public function changeField($table, $field, $field_new, $spec, $keys_new = []); Chris@0: Chris@0: /** Chris@0: * Create a new table from a Drupal table definition. Chris@0: * Chris@0: * @param $name Chris@0: * The name of the table to create. Chris@0: * @param $table Chris@0: * A Schema API table definition array. Chris@0: * Chris@0: * @throws \Drupal\Core\Database\SchemaObjectExistsException Chris@0: * If the specified table already exists. Chris@0: */ Chris@0: public function createTable($name, $table) { Chris@0: if ($this->tableExists($name)) { Chris@0: throw new SchemaObjectExistsException(t('Table @name already exists.', ['@name' => $name])); Chris@0: } Chris@0: $statements = $this->createTableSql($name, $table); Chris@0: foreach ($statements as $statement) { Chris@0: $this->connection->query($statement); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Return an array of field names from an array of key/index column specifiers. Chris@0: * Chris@0: * This is usually an identity function but if a key/index uses a column prefix Chris@0: * specification, this function extracts just the name. Chris@0: * Chris@0: * @param $fields Chris@0: * An array of key/index column specifiers. Chris@0: * Chris@0: * @return Chris@0: * An array of field names. Chris@0: */ Chris@0: public function fieldNames($fields) { Chris@0: $return = []; Chris@0: foreach ($fields as $field) { Chris@0: if (is_array($field)) { Chris@0: $return[] = $field[0]; Chris@0: } Chris@0: else { Chris@0: $return[] = $field; Chris@0: } Chris@0: } Chris@0: return $return; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Prepare a table or column comment for database query. Chris@0: * Chris@0: * @param $comment Chris@0: * The comment string to prepare. Chris@0: * @param $length Chris@0: * Optional upper limit on the returned string length. Chris@0: * Chris@0: * @return Chris@0: * The prepared comment. Chris@0: */ Chris@0: public function prepareComment($comment, $length = NULL) { Chris@0: // Remove semicolons to avoid triggering multi-statement check. Chris@0: $comment = strtr($comment, [';' => '.']); Chris@0: return $this->connection->quote($comment); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Return an escaped version of its parameter to be used as a default value Chris@0: * on a column. Chris@0: * Chris@0: * @param mixed $value Chris@0: * The value to be escaped (int, float, null or string). Chris@0: * Chris@0: * @return string|int|float Chris@0: * The escaped value. Chris@0: */ Chris@0: protected function escapeDefaultValue($value) { Chris@0: if (is_null($value)) { Chris@0: return 'NULL'; Chris@0: } Chris@0: return is_string($value) ? $this->connection->quote($value) : $value; Chris@0: } Chris@0: Chris@17: /** Chris@17: * Ensures that all the primary key fields are correctly defined. Chris@17: * Chris@17: * @param array $primary_key Chris@17: * An array containing the fields that will form the primary key of a table. Chris@17: * @param array $fields Chris@17: * An array containing the field specifications of the table, as per the Chris@17: * schema data structure format. Chris@17: * Chris@17: * @throws \Drupal\Core\Database\SchemaException Chris@17: * Thrown if any primary key field specification does not exist or if they Chris@17: * do not define 'not null' as TRUE. Chris@17: */ Chris@17: protected function ensureNotNullPrimaryKey(array $primary_key, array $fields) { Chris@17: foreach (array_intersect($primary_key, array_keys($fields)) as $field_name) { Chris@17: if (!isset($fields[$field_name]['not null']) || $fields[$field_name]['not null'] !== TRUE) { Chris@17: throw new SchemaException("The '$field_name' field specification does not define 'not null' as TRUE."); Chris@17: } Chris@17: } Chris@17: } Chris@17: Chris@0: }