diff options
Diffstat (limited to 'includes')
-rw-r--r-- | includes/common.inc | 154 | ||||
-rw-r--r-- | includes/database.inc | 157 | ||||
-rw-r--r-- | includes/database.mysql-common.inc | 409 | ||||
-rw-r--r-- | includes/database.mysql.inc | 4 | ||||
-rw-r--r-- | includes/database.mysqli.inc | 3 | ||||
-rw-r--r-- | includes/database.pgsql.inc | 418 | ||||
-rw-r--r-- | includes/module.inc | 39 |
7 files changed, 1179 insertions, 5 deletions
diff --git a/includes/common.inc b/includes/common.inc index feb28de30..fb7f21a41 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -2514,6 +2514,160 @@ function drupal_common_themes() { } /** + * @ingroup schemaapi + * @{ + */ + +/** + * Get the schema defintion of a table, or the whole database schema. + * The returned schema will include any modifications made by any + * module that implements hook_schema_alter(). + * + * @param $name + * The name of the table. If not given, the schema of all tables is returned. + * @param $rebuild + * If true, the schema will be rebuilt instead of retreived from the cache. + */ +function drupal_get_schema($name = NULL, $rebuild = FALSE) { + static $schema = array(); + + if (empty($schema) || $rebuild) { + // Try to load the schema from cache. + if (!$rebuild && $cached = cache_get('schema')) { + $schema = $cached->data; + } + // Otherwise, rebuild the schema cache. + else { + // Load the .schema files. + module_load_all_includes('schema'); + + // Invoke hook_schema for all modules. + foreach (module_implements('schema') as $module) { + $current = module_invoke($module, 'schema'); + _drupal_initialize_schema($module, $current); + $schema = array_merge($current, $schema); + } + + drupal_alter('schema', $schema); + cache_set('schema', $schema); + } + } + + if (!isset($name)) { + return $schema; + } + elseif (isset($schema[$name])) { + return $schema[$name]; + } + else { + return FALSE; + } +} + +/** + * Create all tables that a module defines in its hook_schema(). + * + * Note: This function does not pass the module's schema through + * hook_schema_alter(). The module's tables will be created exactly + * as the module defines them. + * + * @param $module + * The module for which the tables will be created. + */ +function drupal_install_schema($module) { + $schema = drupal_get_schema_unprocessed($module); + _drupal_initialize_schema($module, $schema); + + $ret = array(); + foreach ($schema as $table) { + db_create_table($ret, $table); + } +} + +/** + * Remove all tables that a module defines in its hook_schema(). + * + * Note: This function does not pass the module's schema through + * hook_schema_alter(). The module's tables will be created exactly + * as the module defines them. + * + * @param $module + * The module for which the tables will be removed. + */ +function drupal_uninstall_schema($module) { + $schema = drupal_get_schema_unprocessed($module); + _drupal_initialize_schema($module, $schema); + + $ret = array(); + foreach ($schema as $table) { + db_drop_table($ret, $table['name']); + } +} + +/** + * Returns the unprocessed and unaltered version of a module's schema. + * + * Use this function only if you explicitly need the original + * specification of a schema, as it was defined in a module's + * hook_schema(). No additional default values will be set, + * hook_schema_alter() is not invoked and these unprocessed + * definitions won't be cached. + * + * This function can be used to retrieve a schema specification in + * hook_schema(), so it allows you to derive your tables from existing + * specifications. + * + * It is also used by drupal_install_schema() and + * drupal_uninstall_schema() to ensure that a module's tables are + * created exactly as specified without any changes introduced by a + * module that implements hook_schema_alter(). + * + * @param $module + * The module to which the table belongs. + * @param $table + * The name of the table. If not given, the module's complete schema + * is returned. + */ +function drupal_get_schema_unprocessed($module, $table = NULL) { + // Load the .schema file. + module_load_include('schema', $module); + $schema = module_invoke($module, 'schema'); + + if (!is_null($table) && isset($schema[$table])) { + return $schema[$table]; + } + else { + return $schema; + } +} + +/** + * Fill in required default values for table definitions returned by + * hook_schema(). + * + * @param $module + * The module for which hook_schema() was invoked. + * @param $schema + * The schema definition array as it was returned by the module's + * hook_schema(). + */ +function _drupal_initialize_schema($module, &$schema) { + // Set the name and module key for all tables. + foreach ($schema as $name => $table) { + if (empty($table['module'])) { + $schema[$name]['module'] = $module; + } + if (!isset($table['name'])) { + $schema[$name]['name'] = $name; + } + } +} + +/** + * @} End of "ingroup schemaapi". + */ + +/** * Parse Drupal info file format. * * Files should use an ini-like format to specify values. diff --git a/includes/database.inc b/includes/database.inc index 3e095e334..6e4bdeace 100644 --- a/includes/database.inc +++ b/includes/database.inc @@ -44,6 +44,22 @@ */ /** + * Perform an SQL query and return success or failure. + * + * @param $sql + * A string containing a complete SQL query. %-substitution + * parameters are not supported. + * @return + * An array containing the keys: + * success: a boolean indicating whether the query succeeded + * query: the SQL query executed, passed through check_plain() + */ +function update_sql($sql) { + $result = db_query($sql, true); + return array('success' => $result !== FALSE, 'query' => check_plain($sql)); +} + +/** * Append a database prefix to all tables in a query. * * Queries sent to Drupal should wrap all table names in curly brackets. This @@ -317,3 +333,144 @@ function db_escape_table($string) { * @} End of "defgroup database". */ +/** + * @defgroup schemaapi Schema API + * @{ + * + * A Drupal schema definition is an array structure representing one or + * more tables and their related keys and indexes. A schema is defined by + * hook_schema(), which usually lives in a modulename.schema file. + * + * By implenting hook_schema() and specifying the tables your module + * declares, you can easily create and drop these tables on all + * supported database engines. You don't have to deal with the + * different SQL dialects for table creation and alteration of the + * supported database engines. + * + * hook_schema() should return an array with a key for each table that + * the module defines. + * + * The following keys in the table definition are processed during + * table creation: + * + * - 'fields': An associative array ('fieldname' => specification) + * that describes the table's database columns. The specification + * is also an array. The following specification parameters are defined: + * + * - 'type': The generic datatype: 'varchar', 'int', 'serial' + * 'float', 'numeric', 'text', 'blob' or 'datetime'. Most types + * just map to the according database engine specific + * datatypes. Use 'serial' for auto incrementing fields. This + * will expand to 'int auto_increment' on mysql. + * - 'size': The data size: 'tiny', 'small', 'medium', 'normal', + * 'big'. This is a hint about the largest value the field will + * store and determines which of the database engine specific + * datatypes will be used (e.g. on MySQL, TINYINT vs. INT vs. BIGINT). + * 'normal', the default, selects the base type (e.g. on MySQL, + * INT, VARCHAR, BLOB, etc.). + * + * Not all sizes are available for all data types. See + * db_type_map() for possible combinations. + * - 'not null': If true, no NULL values will be allowed in this + * database column. Defaults to false. + * - 'default': The field's default value. The PHP type of the + * value matters: '', '0', and 0 are all different. If you + * specify '0' as the default value for a type 'int' field it + * will not work because '0' is a string containing the + * character "zero", not an integer. + * - 'length': The maximal length of a type 'varchar' or 'text' + * field. Ignored for other field types. + * - 'unsigned': A boolean indicating whether a type 'int', 'float' + * and 'numeric' only is signed or unsigned. Defaults to + * FALSE. Ignored for other field types. + * - 'precision', 'scale': For type 'numeric' fields, indicates + * the precision (total number of significant digits) and scale + * (decimal digits right of the decimal point). Both values are + * mandatory. Ignored for other field types. + * + * All parameters apart from 'type' are optional except that type + * 'numeric' columns must specify 'precision' and 'scale'. + * + * - 'primary key': An array of one or more key column specifers (see below) + * that form the primary key. + * - 'unique key': An associative array of unique keys ('keyname' => + * specification). Each specification is an array of one or more + * key column specifiers (see below) that form a unique key on the table. + * - 'indexes': An associative array of indexes ('indexame' => + * specification). Each specification is an array of one or more + * key column specifiers (see below) that form an index on the + * table. + * + * A key column specifier is either a string naming a column or an + * array of two elements, column name and length, specifying a prefix + * of the named column. + * + * As an example, here is a SUBSET of the schema definition for + * Drupal's 'node' table. It show four fields (nid, vid, type, and + * title), the primary key on field 'nid', a unique key named 'vid' on + * field 'vid', and two indexes, one named 'nid' on field 'nid' and + * one named 'node_title_type' on the field 'title' and the first four + * bytes of the field 'type': + * + * $schema['node'] = array( + * 'fields' => array( + * 'nid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), + * 'vid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), + * 'type' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''), + 'title' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''), + * ), + * 'primary key' => array('nid'), + * 'unique keys' => array( + * 'vid' => array('vid') + * ), + * 'indexes' => array( + * 'nid' => array('nid'), + * 'node_title_type' => array('title', array('type', 4)), + * ), + * ); + * + * @see drupal_install_schema() + */ + + /** + * Create a new table from a Drupal table definition. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * A valid and processed table schema definition array. + */ +function db_create_table(&$ret, $table) { + $statements = db_create_table_sql($table); + foreach ($statements as $statement) { + $ret[] = update_sql($statement); + } +} + +/** + * Return an array of field names from an array of key/index column + * specifiers. This is usually an identity function but if a + * key/index uses a column prefix specification, this function + * extracts just the name. + * + * @param $fields + * An array of key/index column specifiers. + * @return + * An array of field names. + */ +function db_field_names($fields) { + $ret = array(); + foreach ($fields as $field) { + if (is_array($field)) { + $ret[] = $field[0]; + } + else { + $ret[] = $field; + } + } + return $ret; +} + +/** + * @} End of "defgroup schemaapi". + */ diff --git a/includes/database.mysql-common.inc b/includes/database.mysql-common.inc new file mode 100644 index 000000000..c35c90d45 --- /dev/null +++ b/includes/database.mysql-common.inc @@ -0,0 +1,409 @@ +<?php + +// $Id$ + +/** + * @file + * Functions shared between mysql and mysqli database engines. + */ + +/** + * @ingroup schemaapi + * @{ + */ + +/** + * Generate SQL to create a new table from a Drupal schema definition. + * + * @param $table + * A valid Drupal table definition array. + * @return + * An array of SQL statements to create the table. + */ +function db_create_table_sql($table) { + + if (empty($table['mysql_suffix'])) { + $table['mysql_suffix'] = "/*!40100 DEFAULT CHARACTER SET UTF8 */"; + } + + $sql = "CREATE TABLE {". $table['name'] ."} (\n"; + + // Add the SQL statement for each field. + foreach ($table['fields'] as $name => $field) { + $sql .= _db_create_field_sql($name, _db_process_field($field)) .", \n"; + } + + // Process keys & indexes. + if (!empty($table['primary key'])) { + $sql .= " PRIMARY KEY (". _db_create_key_sql($table['primary key']) ."), \n"; + } + if (!empty($table['unique keys'])) { + foreach ($table['unique keys'] as $key => $fields) + $sql .= " UNIQUE KEY $key (". _db_create_key_sql($fields) ."), \n"; + } + if (!empty($table['indexes'])) { + foreach ($table['indexes'] as $index => $fields) + $sql .= " INDEX $index (". _db_create_key_sql($fields) ."), \n"; + } + + // Remove the last comma and space. + $sql = substr($sql, 0, -3) ."\n) "; + + $sql .= $table['mysql_suffix']; + + return array($sql); +} + +function _db_create_key_sql($fields) { + $ret = array(); + foreach ($fields as $field) { + if (is_array($field)) { + $ret[] = $field[0] .'('. $field[1] .')'; + } + else { + $ret[] = $field; + } + } + return implode(', ', $ret); +} + +/** + * Set database-engine specific properties for a field. + * + * @param $field + * A field description array, as specified in the schema documentation. + */ +function _db_process_field($field) { + + if (!isset($field['size'])) { + $field['size'] = 'normal'; + } + + // Set the correct database-engine specific datatype. + if (!isset($field['mysql_type'])) { + $map = db_type_map(); + $field['mysql_type'] = $map[$field['type'] .':'. $field['size']]; + } + + if ($field['type'] == 'serial') { + $field['auto_increment'] = TRUE; + } + + return $field; +} + +/** + * Create an SQL string for a field to be used in table creation or alteration. + * + * Before passing a field out of a schema definition into this function it has + * to be processed by _db_process_field(). + * + * @param $name + * Name of the field. + * @param $spec + * The field specification, as per the schema data structure format. + */ +function _db_create_field_sql($name, $spec) { + $sql = "`". $name ."` ". $spec['mysql_type']; + + if (isset($spec['length'])) { + $sql .= '('. $spec['length'] .')'; + } + elseif (isset($spec['precision']) && isset($spec['scale'])) { + $sql .= '('. $spec['scale'] .', '. $spec['precision'] .')'; + } + + if (!empty($spec['unsigned'])) { + $sql .= ' unsigned'; + } + + if (!empty($spec['not null'])) { + $sql .= ' NOT NULL'; + } + + if (!empty($spec['auto_increment'])) { + $sql .= ' auto_increment'; + } + + if (isset($spec['default'])) { + if (is_string($spec['default'])) { + $spec['default'] = "'". $spec['default'] ."'"; + } + $sql .= ' DEFAULT '. $spec['default']; + } + + if (empty($spec['not null']) && !isset($spec['default'])) { + $sql .= ' DEFAULT NULL'; + } + + return $sql; +} + +/** + * This maps a generic data type in combination with its data size + * to the engine-specific data type. + */ +function db_type_map() { + // Put :normal last so it gets preserved by array_flip. This makes + // it much easier for modules (such as schema.module) to map + // database types back into schema types. + $map = array( + 'varchar:normal' => 'VARCHAR', + + 'text:tiny' => 'SMALLTEXT', + 'text:small' => 'SMALLTEXT', + 'text:medium' => 'MEDIUMTEXT', + 'text:big' => 'LONGTEXT', + 'text:normal' => 'TEXT', + + 'serial:tiny' => 'TINYINT', + 'serial:small' => 'SMALLINT', + 'serial:medium' => 'MEDIUMINT', + 'serial:big' => 'BIGINT', + 'serial:normal' => 'INT', + + 'int:tiny' => 'TINYINT', + 'int:small' => 'SMALLINT', + 'int:medium' => 'MEDIUMINT', + 'int:big' => 'BIGINT', + 'int:normal' => 'INT', + + 'float:tiny' => 'FLOAT', + 'float:small' => 'FLOAT', + 'float:medium' => 'FLOAT', + 'float:big' => 'DOUBLE', + 'float:normal' => 'FLOAT', + + 'numeric:normal' => 'NUMERIC', + + 'blob:big' => 'LONGBLOB', + 'blob:normal' => 'BLOB', + + 'datetime:normal' => 'DATETIME', + ); + return $map; +} + +/** + * Drop a table. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be dropped. + */ +function db_drop_table(&$ret, $table) { + $ret[] = update_sql('DROP TABLE {'. $table .'}'); +} + +/** + * Add a new field to a table. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * Name of the table to be altered. + * @param $field + * Name of the field to be added. + * @param $spec + * The field specification array, as taken from a schema definition + */ +function db_add_field(&$ret, $table, $field, $spec) { + $query = 'ALTER TABLE {'. $table .'} ADD '. $field .' '; + $query .= _db_create_field_sql($field, _db_process_field($spec)); + $ret[] = update_sql($query); +} + +/** + * Drop a field. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $field + * The field to be dropped. + */ +function db_drop_field(&$ret, $table, $field) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP '. $field); +} + +/** + * Set the default value for a field. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $field + * The field to be altered. + * @param $default + * Default value to be set. NULL for 'default NULL'. + */ +function db_field_set_default(&$ret, $table, $field, $default) { + if ($default == NULL) { + $default = 'NULL'; + } + else { + $default = is_string($default) ? "'$default'" : $default; + } + + $ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER COLUMN '. $field .' SET DEFAULT '. $default); +} + +/** + * Set a field to have no default value. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $field + * The field to be altered. + */ +function db_field_set_no_default(&$ret, $table, $field) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER COLUMN '. $field .' DROP DEFAULT'); +} + +/** + * Add a primary key. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $fields + * Fields for the primary key. + */ +function db_add_primary_key(&$ret, $table, $fields) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} ADD PRIMARY KEY ('. + _db_create_key_sql($fields) .')'); +} + +/** + * Drop the primary key. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + */ +function db_drop_primary_key(&$ret, $table) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP PRIMARY KEY'); +} + +/** + * Add a unique key. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $name + * The name of the key. + * @param $fields + * An array of field names. + */ +function db_add_unique_key(&$ret, $table, $name, $fields) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} ADD UNIQUE KEY '. + $name .' ('. _db_create_key_sql($fields) .')'); +} + +/** + * Drop a unique key. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $name + * The name of the key. + */ +function db_drop_unique_key(&$ret, $table, $name) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP KEY '. $name); +} + +/** + * Add an index. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $name + * The name of the index. + * @param $fields + * An array of field names. + */ +function db_add_index(&$ret, $table, $name, $fields) { + $query = 'ALTER TABLE {'. $table .'} ADD INDEX '. $name .' ('; + + foreach ($fields as $current) { + $query .= $current .', '; + } + + // Remove the last comma, add a closing bracket. + $query = substr($query, 0, -2) .')'; + + $ret[] = update_sql($query); +} + +/** + * Drop an index. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $name + * The name of the index. + */ +function db_drop_index(&$ret, $table, $name) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP INDEX '. $name); +} + +/** + * Change a field definition. + * + * Remember that changing a field definition involves adding a new field + * and dropping an old one. This means that any indices, primary keys and + * sequences from serial-type fields are dropped and might need to be + * recreated. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * Name of the table. + * @param $field + * Name of the field to change. + * @param $field_new + * New name for the field (set to the same as $field if you don't want to change the name). + * @param $spec + * The field specification for the new field. + */ +function db_change_field(&$ret, $table, $field, $field_new, $spec) { + $ret[] = update_sql("ALTER TABLE {". $table ."} CHANGE $field ". + _db_create_field_sql($field_new, _db_process_field($spec))); +} + +/** + * Update a field definition to match its schema. If the field is + * involved in any keys or indexes, recreate them. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * Name of the table. + * @param $field + * Name of the field to update. + */ +function db_update_field(&$ret, $table, $field) { + $spec = drupal_get_schema($table); + db_change_field($ret, $table, $field, $field, $spec['fields'][$field]); +} + +/** + * @} End of "ingroup schemaapi". + */ + +?>
\ No newline at end of file diff --git a/includes/database.mysql.inc b/includes/database.mysql.inc index 1fab19191..166017621 100644 --- a/includes/database.mysql.inc +++ b/includes/database.mysql.inc @@ -11,6 +11,8 @@ * @{ */ +// Include functions shared between mysql and mysqli. +require_once './includes/database.mysql-common.inc'; /** * Report database status. @@ -437,5 +439,3 @@ function db_distinct_field($table, $field, $query) { /** * @} End of "ingroup database". */ - - diff --git a/includes/database.mysqli.inc b/includes/database.mysqli.inc index 04f8cc9aa..ad58a6f24 100644 --- a/includes/database.mysqli.inc +++ b/includes/database.mysqli.inc @@ -15,6 +15,9 @@ * @{ */ +// Include functions shared between mysql and mysqli. +require_once './includes/database.mysql-common.inc'; + /** * Report database status. */ diff --git a/includes/database.pgsql.inc b/includes/database.pgsql.inc index be5e4447a..3e050d8d1 100644 --- a/includes/database.pgsql.inc +++ b/includes/database.pgsql.inc @@ -436,4 +436,422 @@ function db_distinct_field($table, $field, $query) { * @} End of "ingroup database". */ +/** + * @ingroup schemaapi + * @{ + */ + +/** + * This maps a generic data type in combination with its data size + * to the engine-specific data type. + */ +function db_type_map() { + // Put :normal last so it gets preserved by array_flip. This makes + // it much easier for modules (such as schema.module) to map + // database types back into schema types. + $map = array( + 'varchar:normal' => 'varchar', + + 'text:tiny' => 'text', + 'text:small' => 'text', + 'text:medium' => 'text', + 'text:big' => 'text', + 'text:normal' => 'text', + + 'int:tiny' => 'smallint', + 'int:small' => 'smallint', + 'int:medium' => 'int', + 'int:big' => 'bigint', + 'int:normal' => 'int', + + 'float:tiny' => 'real', + 'float:small' => 'real', + 'float:medium' => 'real', + 'float:big' => 'double precision', + 'float:normal' => 'real', + + 'numeric:normal' => 'numeric', + + 'blob:big' => 'bytea', + 'blob:normal' => 'bytea', + + 'datetime:normal' => 'timestamp', + + 'serial:tiny' => 'serial', + 'serial:small' => 'serial', + 'serial:medium' => 'serial', + 'serial:big' => 'bigserial', + 'serial:normal' => 'serial', + ); + return $map; +} + +/** + * Generate SQL to create a new table from a Drupal schema definition. + * + * @param $table + * A valid Drupal table definition array. + * @return + * An array of SQL statements to create the table. + */ +function db_create_table_sql($table) { + $sql_fields = array(); + foreach ($table['fields'] as $name => $field) { + $sql_fields[] = _db_create_field_sql($name, _db_process_field($field)); + } + + $sql_keys = array(); + if (isset($table['primary key']) && is_array($table['primary key'])) { + $sql_keys[] = 'PRIMARY KEY ('. implode(', ', $table['primary key']) .')'; + } + if (isset($table['unique keys']) && is_array($table['unique keys'])) { + foreach ($table['unique keys'] as $keyname => $key) { + $sql_keys[] = 'CONSTRAINT {'. $table['name'] .'}_'. $keyname .'_key UNIQUE ('. implode(', ', $key) .')'; + } + } + + $sql = "CREATE TABLE {". $table['name'] ."} (\n\t"; + $sql .= implode(",\n\t", $sql_fields); + if (count($sql_keys) > 0) { + $sql .= ",\n\t"; + } + $sql .= implode(",\n\t", $sql_keys); + $sql .= "\n)"; + $statements[] = $sql; + + if (isset($table['indexes']) && is_array($table['indexes'])) { + foreach ($table['indexes'] as $keyname => $key) { + $statements[] = _db_create_index_sql($table['name'], $keyname, $key); + } + } + + return $statements; +} + +function _db_create_index_sql($table, $name, $fields) { + $query = 'CREATE INDEX {'. $table .'}_'. $name .'_idx ON {'. $table .'} ('; + $query .= _db_create_key_sql($fields) .')'; + return $query; +} + +function _db_create_key_sql($fields) { + $ret = array(); + foreach ($fields as $field) { + if (is_array($field)) { + $ret[] = 'substr('. $field[0] .', 1, '. $field[1] .')'; + } + else { + $ret[] = $field; + } + } + return implode(', ', $ret); +} + +/** + * Set database-engine specific properties for a field. + * + * @param $field + * A field description array, as specified in the schema documentation. + */ +function _db_process_field($field) { + if (!isset($field['size'])) { + $field['size'] = 'normal'; + } + // Set the correct database-engine specific datatype. + if (!isset($field['pgsql_type'])) { + $map = db_type_map(); + $field['pgsql_type'] = $map[$field['type'] .':'. $field['size']]; + } + if ($field['type'] == 'serial') { + unset($field['not null']); + } + return $field; +} + +/** + * Create an SQL string for a field to be used in table creation or alteration. + * + * Before passing a field out of a schema definition into this function it has + * to be processed by _db_process_field(). + * + * @param $name + * Name of the field. + * @param $spec + * The field specification, as per the schema data structure format. + */ +function _db_create_field_sql($name, $spec) { + $sql = $name .' '. $spec['pgsql_type']; + + if ($spec['type'] == 'serial') { + unset($spec['not null']); + } + if (!empty($spec['unsigned'])) { + if ($spec['type'] == 'serial') { + $sql .= " CHECK ($name >= 0)"; + } + else { + $sql .= '_unsigned'; + } + } + + if (!empty($spec['length'])) { + $sql .= '('. $spec['length'] .')'; + } + elseif (isset($spec['precision']) && isset($spec['scale'])) { + $sql .= '('. $spec['scale'] .', '. $spec['precision'] .')'; + } + + if (isset($spec['not null']) && $spec['not null']) { + $sql .= ' NOT NULL'; + } + if (isset($spec['default'])) { + $default = is_string($spec['default']) ? "'". $spec['default'] ."'" : $spec['default']; + $sql .= " default $default"; + } + + return $sql; +} + +/** + * Drop a table. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be dropped. + */ +function db_drop_table(&$ret, $table) { + $ret[] = update_sql('DROP TABLE {'. $table .'}'); +} + +/** + * Add a new field to a table. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * Name of the table to be altered. + * @param $field + * Name of the field to be added. + * @param $spec + * The field specification array, as taken from a schema definition + */ +function db_add_field(&$ret, $table, $field, $spec) { + $query = 'ALTER TABLE {'. $table .'} ADD COLUMN '; + $query .= _db_create_field_sql($field, _db_process_field($spec)); + $ret[] = update_sql($query); +} + +/** + * Drop a field. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $field + * The field to be dropped. + */ +function db_drop_field(&$ret, $table, $field) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP COLUMN '. $field); +} + +/** + * Set the default value for a field. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $field + * The field to be altered. + * @param $default + * Default value to be set. NULL for 'default NULL'. + */ +function db_field_set_default(&$ret, $table, $field, $default) { + if ($default == NULL) { + $default = 'NULL'; + } + else { + $default = is_string($default) ? "'$default'" : $default; + } + + $ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER COLUMN '. $field .' SET DEFAULT '. $default); +} + +/** + * Set a field to have no default value. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $field + * The field to be altered. + */ +function db_field_set_no_default(&$ret, $table, $field) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER COLUMN '. $field .' DROP DEFAULT'); +} + +/** + * Add a primary key. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $fields + * Fields for the primary key. + */ +function db_add_primary_key(&$ret, $table, $fields) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} ADD PRIMARY KEY ('. + implode(',', $fields) .')'); +} + +/** + * Drop the primary key. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + */ +function db_drop_primary_key(&$ret, $table) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP CONSTRAINT {'. $table .'}_pkey'); +} + +/** + * Add a unique key. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $name + * The name of the key. + * @param $fields + * An array of field names. + */ +function db_add_unique_key(&$ret, $table, $name, $fields) { + $name = '{'. $table .'}_'. $name .'_key'; + $ret[] = update_sql('ALTER TABLE {'. $table .'} ADD CONSTRAINT '. + $name .' UNIQUE ('. implode(',', $fields) .')'); +} + +/** + * Drop a unique key. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $name + * The name of the key. + */ +function db_drop_unique_key(&$ret, $table, $name) { + $name = '{'. $table .'}_'. $name .'_key'; + $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP CONSTRAINT '. $name); +} + +/** + * Add an index. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $name + * The name of the index. + * @param $fields + * An array of field names. + */ +function db_add_index(&$ret, $table, $name, $fields) { + $ret[] = update_sql(_db_create_index_sql($table, $name, $fields)); +} + +/** + * Drop an index. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be altered. + * @param $name + * The name of the index. + */ +function db_drop_index(&$ret, $table, $name) { + $name = '{'. $table .'}_'. $name .'_idx'; + $ret[] = update_sql('DROP INDEX '. $name); +} + +/** + * Change a field definition. + * + * Remember that changing a field definition involves adding a new field + * and dropping an old one. This means that any indices, primary keys and + * sequences from serial-type fields are dropped and might need to be + * recreated. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * Name of the table. + * @param $field + * Name of the field to change. + * @param $field_new + * New name for the field (set to the same as $field if you don't want to change the name). + * @param $spec + * The field specification for the new field. + */ +function db_change_field(&$ret, $table, $field, $field_new, $spec) { + $ret[] = update_sql("ALTER TABLE {". $table ."} RENAME $field TO ". $field ."_old"); + $not_null = isset($spec['not null']) ? $spec['not null'] : FALSE; + unset($spec['not null']); + db_add_field($ret, $table, "$field_new", $spec); + $ret[] = update_sql("UPDATE {". $table ."} SET $field_new = ". $field ."_old"); + if ($not_null) { + $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $field_new SET NOT NULL"); + } + db_drop_field($ret, $table, $field .'_old'); +} + +/** + * Update a field definition to match its schema. If the field is + * involved in any keys or indexes, recreate them if necessary. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * Name of the table. + * @param $field + * Name of the field to update. + */ +function db_update_field(&$ret, $table, $field) { + $spec = drupal_get_schema($table); + + db_change_field($ret, $table, $field, $field, $spec['fields'][$field]); + if (isset($spec['primary key'])) { + if (array_search($field, db_field_names($spec['primary key'])) !== FALSE) { + db_add_primary_key($ret, $table, $spec['primary key']); + } + } + if (isset($spec['unique keys'])) { + foreach ($spec['unique keys'] as $name => $fields) { + if (array_search($field, db_field_names($fields)) !== FALSE) { + db_add_unique_key($ret, $table, $fields); + } + } + } + if (isset($spec['indexes'])) { + foreach ($spec['indexes'] as $name => $fields) { + if (array_search($field, db_field_names($fields)) !== FALSE) { + db_add_index($ret, $table, $fields); + } + } + } +} + +/** + * @} End of "ingroup schemaapi". + */ diff --git a/includes/module.inc b/includes/module.inc index 5ff8f5575..dcec75f5c 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -189,9 +189,42 @@ function module_load_install($module) { // Make sure the installation API is available include_once './includes/install.inc'; - $install_file = './'. drupal_get_path('module', $module) .'/'. $module .'.install'; - if (is_file($install_file)) { - include_once $install_file; + module_load_include('install', $module); +} + +/** + * Load a module include file. + * + * @param $type + * The include file's type (file extension). + * @param $module + * The module to which the include file belongs. + * @param $name + * Optionally, specify the file name. If not set, the module's name is used. + */ +function module_load_include($type, $module, $name = NULL) { + if (empty($name)) { + $name = $module; + } + + $file = './'. drupal_get_path('module', $module) ."/$name.$type"; + + if (is_file($file)) { + require_once $file; + } + else { + return FALSE; + } +} + +/** + * Load an include file for each of the modules that have been enabled in + * the system table. + */ +function module_load_all_includes($type, $name = NULL) { + $modules = module_list(); + foreach ($modules as $module) { + module_load_include($type, $module, $name); } } |