=== modified file 'includes/database/database.inc' --- includes/database/database.inc 2008-08-21 19:36:35 +0000 +++ includes/database/database.inc 2008-08-26 21:17:00 +0000 @@ -54,59 +54,59 @@ define('DB_ERROR', 'a515ac9c2796ca0e23ad * database servers, so that is abstracted into db_query_range() arguments. * Finally, note the PDO-based ability to foreach() over the result set. * - * - * All queries are passed as a prepared statement string. A + * + * All queries are passed as a prepared statement string. A * prepared statement is a "template" of a query that omits literal or variable - * values in favor of placeholders. The values to place into those - * placeholders are passed separately, and the database driver handles + * values in favor of placeholders. The values to place into those + * placeholders are passed separately, and the database driver handles * inserting the values into the query in a secure fashion. That means you * should never quote or string-escape a value to be inserted into the query. - * + * * There are two formats for placeholders: named and unnamed. Named placeholders - * are strongly preferred in all cases as they are more flexible and + * are strongly preferred in all cases as they are more flexible and * self-documenting. - * + * * Named placeholders begin with a colon followed by a unique string. Example: * @code * SELECT nid, title FROM {node} WHERE uid=:uid * @endcode - * + * * ":uid" is a placeholder that will be replaced with a literal value when * the query is executed. A given placeholder label cannot be repeated in a * given query, even if the value should be the same. When using named * placeholders, the array of arguments to the query must be an associative * array where keys are a placeholder label (e.g., :uid) and the value is the * corresponding value to use. The array may be in any order. - * + * * Unnamed placeholders are simply a question mark. Example: * @code * SELECT nid, title FROM {node} WHERE uid=? * @endcode - * + * * In this case, the array of arguments must be an indexed array of values to - * use in the exact same order as the placeholders in the query. - * + * use in the exact same order as the placeholders in the query. + * * Note that placeholders should be a "complete" value. For example, when * running a LIKE query the SQL wildcard character, %, should be part of the * value, not the query itself. Thus, the following is incorrect: - * + * * @code * SELECT nid, title FROM {node} WHERE title LIKE :title% * @endcode - * + * * It should instead read: - * + * * @code * SELECT nid, title FROM {node} WHERE title LIKE :title * @endcode - * + * * and the value for :title should include a % as appropriate. Again, note the * lack of quotation marks around :title. Because the value is not inserted * into the query as one big string but as an explicitly separate value, the - * database server knows where the query ends and a value begins. That is - * considerably more secure against SQL injection than trying to remember + * database server knows where the query ends and a value begins. That is + * considerably more secure against SQL injection than trying to remember * which values need quotation marks and string escaping and which don't. - * + * * * INSERT, UPDATE, and DELETE queries need special care in order to behave * consistently across all different databases. Therefore, they use a special @@ -120,7 +120,7 @@ define('DB_ERROR', 'a515ac9c2796ca0e23ad * db_insert('my_table')->fields($fields)->execute(); * @endcode * This method allows databases that need special data type handling to do so, - * while also allowing optimizations such as multi-insert queries. UPDATE and + * while also allowing optimizations such as multi-insert queries. UPDATE and * DELETE queries have a similar pattern. */ @@ -155,48 +155,48 @@ abstract class DatabaseConnection extend /** * Return the default query options for any given query. * - * A given query can be customized with a number of option flags in an + * A given query can be customized with a number of option flags in an * associative array. * - * target - The database "target" against which to execute a query. Valid - * values are "default" or "slave". The system will first try to open a - * connection to a database specified with the user-supplied key. If one - * is not available, it will silently fall back to the "default" target. - * If multiple databases connections are specified with the same target, + * target - The database "target" against which to execute a query. Valid + * values are "default" or "slave". The system will first try to open a + * connection to a database specified with the user-supplied key. If one + * is not available, it will silently fall back to the "default" target. + * If multiple databases connections are specified with the same target, * one will be selected at random for the duration of the request. - * + * * fetch - This element controls how rows from a result set will be returned. * legal values include PDO::FETCH_ASSOC, PDO::FETCH_BOTH, PDO::FETCH_OBJ, * PDO::FETCH_NUM, or a string representing the name of a class. If a string * is specified, each record will be fetched into a new object of that class. * The behavior of all other values is defined by PDO. See * http://www.php.net/PDOStatement-fetch - * + * * return - Depending on the type of query, different return values may be * meaningful. This directive instructs the system which type of return - * value is desired. The system will generally set the correct value + * value is desired. The system will generally set the correct value * automatically, so it is extremely rare that a module developer will ever * need to specify this value. Setting it incorrectly will likely lead to * unpredictable results or fatal errors. Legal values include: - * + * * Database::RETURN_STATEMENT - Return the prepared statement object for the * query. This is usually only meaningful for SELECT queries, where the * statement object is how one accesses the result set returned by the query. - * - * Database::RETURN_AFFECTED - Return the number of rows affected by an - * UPDATE or DELETE query. Be aware that means the number of rows + * + * Database::RETURN_AFFECTED - Return the number of rows affected by an + * UPDATE or DELETE query. Be aware that means the number of rows * actually changed, not the number of rows matched by the WHERE clause. - * + * * Database::RETURN_INSERT_ID - Return the sequence ID (primary key) * created by an INSERT statement on a table that contains a serial column. - * + * * 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. * - * throw_exception - By default, the database system will catch any errors - * on a query as an Exception, log it, and then rethrow it so that code - * further up the call chain can take an appropriate action. To supress + * throw_exception - By default, the database system will catch any errors + * on a query as an Exception, log it, and then rethrow it so that code + * further up the call chain can take an appropriate action. To supress * that behavior and simply return NULL on failure, set this option to FALSE. * * @return @@ -216,7 +216,7 @@ abstract class DatabaseConnection extend * * Queries sent to Drupal should wrap all table names in curly brackets. This * function searches for this syntax and adds Drupal's table prefix to all - * tables, allowing Drupal to coexist with other systems in the same database + * tables, allowing Drupal to coexist with other systems in the same database * if necessary. * * @param $sql @@ -250,18 +250,24 @@ abstract class DatabaseConnection extend /** * Prepare a query string and return the prepared statement. - * + * * This method statically caches prepared statements, reusing them when * possible. It also prefixes tables names enclosed in curly-braces. * * @param $query * The query string as SQL, with curly-braces surrounding the * table names. + * @param $reset + * Reset the prepared statement static cache. * @return * A PDO prepared statement ready for its execute() method. */ - protected function prepareQuery($query) { + protected function prepareQuery($query, $reset = FALSE) { static $statements = array(); + if ($reset) { + $statements = array(); + return; + } $query = self::prefixTables($query); if (empty($statements[$query])) { $statements[$query] = parent::prepare($query); @@ -271,10 +277,10 @@ abstract class DatabaseConnection extend /** * Create the appropriate sequence name for a given table and serial field. - * + * * This information is exposed to all database drivers, although it is only * useful on some of them. This method is table prefix-aware. - * + * * @param $table * The table name to use for the sequence. * @param $field @@ -285,7 +291,7 @@ abstract class DatabaseConnection extend public function makeSequenceName($table, $field) { return $this->prefixTables('{'. $table .'}_'. $field .'_seq'); } - + /** * Executes a query string against the database. * @@ -293,16 +299,16 @@ abstract class DatabaseConnection extend * of every query. All queries executed by Drupal are executed as * PDO prepared statements. This method statically caches those * prepared statements, reusing them when possible. - * + * * @param $query * The query to execute. In most cases this will be a string containing - * an SQL query with placeholders. An already-prepared instance of + * an SQL query with placeholders. An already-prepared instance of * DatabaseStatement may also be passed in order to allow calling code * to manually bind variables to a query. If a DatabaseStatement object * is passed, the $args array will be ignored. - * - * It is extremely rare that module code will need to pass a statement - * object to this method. It is used primarily for database drivers for + * + * It is extremely rare that module code will need to pass a statement + * object to this method. It is used primarily for database drivers for * databases that require special LOB field handling. * @param $args * An array of arguments for the prepared statement. If the prepared @@ -323,7 +329,7 @@ abstract class DatabaseConnection extend // Use default values if not already set. $options += $this->defaultOptions(); - + try { // We allow either a pre-bound statement object or a literal string. // In either case, we want to end up with an executed statement object. @@ -335,7 +341,7 @@ abstract class DatabaseConnection extend $stmt = $this->prepareQuery($query); $stmt->execute($args, $options); } - + // Depending on the type of query we may need to return a different value. // See DatabaseConnection::defaultOptions() for a description of each value. switch ($options['return']) { @@ -516,11 +522,11 @@ abstract class DatabaseConnection extend */ public function startTransaction($required = FALSE) { static $class_type; - + if ($required && !$this->supportsTransactions()) { throw new TransactionsNotSupportedException(); } - + if (empty($class_type)) { $class_type = 'DatabaseTransaction_' . $this->driver(); if (!class_exists($class_type)) { @@ -599,7 +605,7 @@ abstract class DatabaseConnection extend */ abstract public function databaseType(); - + /** * Gets any special processing requirements for the condition operator. * @@ -629,13 +635,13 @@ abstract class Database { /** * Flag to indicate a query call should simply return NULL. - * + * * This is used for queries that have no reasonable return value * anyway, such as INSERT statements to a table without a serial * primary key. */ const RETURN_NULL = 0; - + /** * Flag to indicate a query call should return the prepared statement. */ @@ -645,12 +651,12 @@ abstract class Database { * Flag to indicate a query call should return the number of affected rows. */ const RETURN_AFFECTED = 2; - + /** * Flag to indicate a query call should return the "last insert id". */ const RETURN_INSERT_ID = 3; - + /** * An nested array of all active connections. It is keyed by database name and target. * @@ -712,7 +718,7 @@ abstract class Database { * TRUE if there is at least one database connection established, FALSE otherwise. */ final public static function isActiveConnection() { - return !empty(self::$connections); + return !empty(self::$activeKey) && !empty(self::$connections) && !empty(self::$connections[self::$activeKey]); } /** @@ -735,7 +741,7 @@ abstract class Database { /** * Process the configuration file for database information. - * + * * Because the config file accepts various "fallback" configurations, we have * to parse the configuration array out into a standardized "complete" form, * applying defaults where necessary. @@ -760,8 +766,8 @@ abstract class Database { } foreach ($databaseInfo[$index] as $target => $value) { - // If there is no "driver" property, then we assume it's an array of - // possible connections for this target. Pick one at random. That + // If there is no "driver" property, then we assume it's an array of + // possible connections for this target. Pick one at random. That // allows us to have, for example, multiple slave servers. if (empty($value['driver'])) { $databaseInfo[$index][$target] = $databaseInfo[$index][$target][mt_rand(0, count($databaseInfo[$index][$target]) - 1)]; @@ -771,7 +777,7 @@ abstract class Database { self::$databaseInfo = $databaseInfo; } - + /** * Gets information on the specified database connection. * @@ -786,7 +792,7 @@ abstract class Database { if (!empty(self::$databaseInfo[$key])) { return self::$databaseInfo[$key]; } - + } /** @@ -797,7 +803,7 @@ abstract class Database { * is "default". * @param $target * The database target to open. If the specified target does not exist, - * the "default" target will be used instead. + * the "default" target will be used instead. */ final protected static function openConnection($key, $target) { global $db_prefix; @@ -821,7 +827,7 @@ abstract class Database { if (!$driver = self::$databaseInfo[$key][$target]['driver']) { throw new Exception('Drupal is not set up'); } - + // We cannot rely on the registry yet, because the registry requires // an open database connection. $driver_class = 'DatabaseConnection_' . $driver; @@ -847,8 +853,8 @@ abstract class Database { /** * Exception to mark databases that do not support transations. - * - * This exception will be thrown when a transaction is started that does not + * + * This exception will be thrown when a transaction is started that does not * allow for the "silent fallback" of no transaction and the database connection * in use does not support transactions. The calling code must then take * appropriate action. @@ -908,7 +914,7 @@ class DatabaseTransaction { /** * Track the number of "layers" of transactions currently active. - * + * * On many databases transactions cannot nest. Instead, we track * nested calls to transactions and collapse them into a single * transaction. @@ -916,7 +922,7 @@ class DatabaseTransaction { * @var int */ protected static $layers = 0; - + public function __construct(DatabaseConnection $connection) { $this->connection = $connection; $this->supportsTransactions = $connection->supportsTransactions(); @@ -924,7 +930,7 @@ class DatabaseTransaction { if (self::$layers == 0 && $this->supportsTransactions) { $connection->beginTransaction(); } - + ++self::$layers; } @@ -971,9 +977,9 @@ class DatabaseTransaction { /** * Prepared statement class. * - * PDO allows us to extend the PDOStatement class to provide additional - * functionality beyond that offered by default. We do need extra - * functionality. By default, this class is not driver-specific. If a given + * PDO allows us to extend the PDOStatement class to provide additional + * functionality beyond that offered by default. We do need extra + * functionality. By default, this class is not driver-specific. If a given * driver needs to set a custom statement class, it may do so in its constructor. * * @link http://us.php.net/pdostatement @@ -982,7 +988,7 @@ class DatabaseStatement extends PDOState /** * Reference to the database connection object for this statement. - * + * * The name $dbh is inherited from PDOStatement. * * @var DatabaseConnection @@ -1033,8 +1039,8 @@ class DatabaseStatement extends PDOState /** * Returns an entire result set as an associative array keyed by the named field. - * - * If the given key appears multiple times, later records will overwrite + * + * If the given key appears multiple times, later records will overwrite * earlier ones. * * Note that this method will run the result set to the end. @@ -1042,7 +1048,7 @@ class DatabaseStatement extends PDOState * @param $key * The name of the field on which to index the array. * @param $fetch - * The fetchmode to use. If set to PDO::FETCH_ASSOC, PDO::FETCH_NUM, or + * The fetchmode to use. If set to PDO::FETCH_ASSOC, PDO::FETCH_NUM, or * PDO::FETCH_BOTH the returned value with be an array of arrays. For any * other value it will be an array of objects. * @return @@ -1150,7 +1156,7 @@ function db_query($query, $args = array( array_shift($args); } list($query, $args, $options) = _db_query_process_args($query, $args, $options); - + return Database::getActiveConnection($options['target'])->query($query, $args, $options); } @@ -1184,7 +1190,7 @@ function db_query_range($query, $args, $ $from = array_pop($args); } list($query, $args, $options) = _db_query_process_args($query, $args, $options); - + return Database::getActiveConnection($options['target'])->queryRange($query, $args, $from, $count, $options); } @@ -1214,7 +1220,7 @@ function db_query_temporary($query, $arg array_shift($args); } list($query, $args, $options) = _db_query_process_args($query, $args, $options); - + return Database::getActiveConnection($options['target'])->queryTemporary($query, $args, $tablename, $options); } @@ -1356,7 +1362,9 @@ function db_escape_table($table) { * query: the SQL query executed, passed through check_plain() */ function update_sql($sql) { - $result = Database::getActiveConnection()->query($sql/*, array(true)*/); + $activeConnection = Database::getActiveConnection(); + $result = $activeConnection->query($sql); + $activeConnection->prepareQuery('', TRUE); return array('success' => $result !== FALSE, 'query' => check_plain($sql)); } @@ -1815,11 +1823,11 @@ function _db_need_install() { /** * Backward-compatibility utility. - * + * * This function should be removed after all queries have been converted * to the new API. It is temporary only. - * - * @todo Remove this once the query conversion is complete. + * + * @todo Remove this once the query conversion is complete. */ function _db_query_process_args($query, $args, $options) { @@ -1829,7 +1837,7 @@ function _db_query_process_args($query, if (empty($options['target'])) { $options['target'] = 'default'; } - + // Temporary backward-compatibliity hacks. Remove later. $old_query = $query; $query = str_replace(array('%n', '%d', '%f', '%b', "'%s'", '%s'), '?', $old_query); === added directory 'includes/database/sqlite' === added file 'includes/database/sqlite/database.inc' --- includes/database/sqlite/database.inc 1970-01-01 00:00:00 +0000 +++ includes/database/sqlite/database.inc 2008-08-26 21:14:33 +0000 @@ -0,0 +1,66 @@ +query($query . ' LIMIT ' . $from . ', ' . $count, $args, $options); + } + + public function queryTemporary($query, Array $args, $tablename) { + $query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE ' . $tablename . ' SELECT', $this->prefixTables($query)); + + return $this->query($query, $args, $options); + } + + public function driver() { + return 'sqlite'; + } + + public function databaseType() { + return 'sqlite'; + } + + public function supportsTransactions() { + return TRUE;; + } + + public function escapeTable($table) { + return preg_replace('/[^A-Za-z0-9_]+/', '', $table); + } + + public function mapConditionOperator($operator) { + // We don't want to override any of the defaults. + return NULL; + } + + /** + * @todo Remove this as soon as db_rewrite_sql() has been exterminated. + */ + public function distinctField($table, $field, $query) { + $field_to_select = 'DISTINCT(' . $table . '.' . $field . ')'; + // (?insertFields, $this->defaultFields)) { + throw new PDOException('You may not specify the same field to have a value and a schema-default value.'); + } + + $last_insert_id = 0; + + $max_placeholder = 0; + $values = array(); + foreach ($this->insertValues as $insert_values) { + foreach ($insert_values as $value) { + $values[':db_insert_placeholder_' . $max_placeholder++] = $value; + } + } + + $last_insert_id = $this->connection->query((string)$this, $values, $this->queryOptions); + + // Re-initialize the values array so that we can re-use this query. + $this->insertValues = array(); + + return $last_insert_id; + } + + public function __toString() { + + // Default fields are always placed first for consistency. + $insert_fields = array_merge($this->defaultFields, $this->insertFields); + + $query = "INSERT INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES '; + + $max_placeholder = 0; + $values = array(); + if (count($this->insertValues)) { + foreach ($this->insertValues as $insert_values) { + $placeholders = array(); + + // Default fields aren't really placeholders, but this is the most convenient + // way to handle them. + $placeholders = array_pad($placeholders, count($this->defaultFields), 'default'); + + $new_placeholder = $max_placeholder + count($insert_values); + for ($i = $max_placeholder; $i < $new_placeholder; ++$i) { + $placeholders[] = ':db_insert_placeholder_'. $i; + } + $max_placeholder = $new_placeholder; + $values[] = '('. implode(', ', $placeholders) .')'; + } + } + else { + // If there are no values, then this is a default-only query. We still need to handle that. + $placeholders = array_fill(0, count($this->defaultFields), 'default'); + $values[] = '(' . implode(', ', $placeholders) .')'; + } + + $query .= implode(', ', $values); + + return $query; + } +} + +class MergeQuery_sqlite extends MergeQuery { + + public function execute() { + if ($this->expressionFields || $this->updateFields || $this->excludeFields) { + return parent::execute(); + } + + $insert_fields = $this->insertFields + $this->keyFields; + + $max_placeholder = 0; + $values = array(); + // We assume that the order here is the same as in __toString(). If that's + // not the case, then we have serious problems. + foreach ($insert_fields as $value) { + $values[':db_insert_placeholder_' . $max_placeholder++] = $value; + } + + $last_insert_id = $this->connection->query((string)$this, $values, $this->queryOptions); + + return $last_insert_id; + } + + + public function __toString() { + if ($this->expressionFields || $this->updateFields || $this->excludeFields) { + return parent::_toString(); + } + + $insert_fields = $this->insertFields + $this->keyFields; + + $query = "INSERT OR REPLACE INTO {" . $this->table . '} (' . implode(', ', array_keys($insert_fields)) . ') VALUES '; + + $max_placeholder = 0; + $values = array(); + // We don't need the $field, but this is a convenient way to count. + foreach ($insert_fields as $field) { + $values[] = ':db_insert_placeholder_' . $max_placeholder++; + } + $query .= '(' . implode(', ', $values) . ')'; + return $query; + } +} + +/** + * @} End of "ingroup database". + */ === added file 'includes/database/sqlite/schema.inc' --- includes/database/sqlite/schema.inc 1970-01-01 00:00:00 +0000 +++ includes/database/sqlite/schema.inc 2008-08-25 15:32:41 +0000 @@ -0,0 +1,328 @@ +connection->query("SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE '{" . $table . "}'", array(), array())->fetchField(); + } + + public function columnExists($table, $column) { + foreach($this->connection->query('PRAGMA INFO ({'. $this->escapeTable($table) .'})')->fetchAll(PDO::FETCH_OBJ) as $row) { + if ($row->name == $column) { + return TRUE; + } + } + return FALSE; + } + + /** + * Generate SQL to create a new table from a Drupal schema definition. + * + * @param $name + * The name of the table to create. + * @param $table + * A Schema API table definition array. + * @return + * An array of SQL statements to create the table. + */ + public function createTableSql($name, $table) { + $sql = array(); + $sql[] = "CREATE TABLE {". $name ."} (\n". $this->buildColsSql($name, $table) ."\n);\n"; + return array_merge($sql, $this->createIndexSql($name, $table)); + } + + protected function createIndexSql($tablename, $schema) { + $sql = array(); + if (!empty($schema['unique keys'])) { + foreach ($schema['unique keys'] as $key => $fields) { + $sql[] = 'CREATE UNIQUE INDEX {'. $tablename .'}_'. $key .' ON {'. $tablename .'} ('. $this->createKeySql($fields) ."); \n"; + } + } + if (!empty($schema['indexes'])) { + foreach ($schema['indexes'] as $index => $fields) { + $sql[] = 'CREATE INDEX {'. $tablename .'}_'. $index .' ON {'. $tablename .'} ('. $this->createKeySql($fields) ."); \n"; + } + } + return $sql; + } + + protected function buildColsSql($tablename, $schema) { + $sql = ""; + + // Add the SQL statement for each field. + foreach ($schema['fields'] as $name => $field) { + if ($field['type'] == 'serial') { + if (($key = array_search($name, $schema['primary key'])) !== false) { + unset($schema['primary key'][$key]); + } + } + $sql .= $this->createFieldSql($name, $this->processField($field)) .", \n"; + } + + // Process keys. + if (!empty($schema['primary key'])) { + $sql .= " PRIMARY KEY (". $this->createKeySql($schema['primary key']) ."), \n"; + } + + // Remove the last comma and space. + $sql = substr($sql, 0, -3); + return $sql; + } + + protected function createKeySql($fields) { + $ret = array(); + foreach ($fields as $field) { + if (is_array($field)) { + $ret[] = $field[0]; + } + 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. + */ + protected function processField($field) { + if (!isset($field['size'])) { + $field['size'] = 'normal'; + } + // Set the correct database-engine specific datatype. + if (!isset($field['sqlite_type'])) { + $map = db_type_map(); + $field['sqlite_type'] = $map[$field['type'] .':'. $field['size']]; + } + + if ($field['type'] == 'serial') { // this should check if field is a primary key + $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_processField(). + * + * @param $name + * Name of the field. + * @param $spec + * The field specification, as per the schema data structure format. + */ + protected function createFieldSql($name, $spec) { + if (!empty($spec['auto_increment'])) { + $sql = "". $name ." INTEGER PRIMARY KEY"; + } + else { + $sql = "". $name ." ". $spec['sqlite_type']; + + if (isset($spec['length'])) { + $sql .= '('. $spec['length'] .')'; + } + //TODO: does scale apply for sqlite ?? + elseif (isset($spec['precision']) && isset($spec['scale'])) { + $sql .= '('. $spec['scale'] .', '. $spec['precision'] .')'; + } + + if (!empty($spec['not null'])) { + $sql .= ' NOT NULL'; + } + + 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. + */ + public function getFieldTypeMap() { + // 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. + /* + a VARCHAR(10), + b NVARCHAR(15), + c TEXT, + d INTEGER, + e FLOAT, + f BOOLEAN, + g CLOB, + h BLOB, + i TIMESTAMP, + j NUMERIC(10,5) + k VARYING CHARACTER (24), + l NATIONAL VARYING CHARACTER(16) + */ + $map = array( + 'varchar:normal' => 'VARCHAR', + + 'text:tiny' => 'TEXT', + 'text:small' => 'TEXT', + 'text:medium' => 'TEXT', + 'text:big' => 'TEXT', + 'text:normal' => 'TEXT', + + 'serial:tiny' => 'INTEGER', + 'serial:small' => 'INTEGER', + 'serial:medium' => 'INTEGER', + 'serial:big' => 'INTEGER', + 'serial:normal' => 'INTEGER', + + 'int:tiny' => 'INTEGER', + 'int:small' => 'INTEGER', + 'int:medium' => 'INTEGER', + 'int:big' => 'INTEGER', + 'int:normal' => 'INTEGER', + + 'float:tiny' => 'FLOAT', + 'float:small' => 'FLOAT', + 'float:medium' => 'FLOAT', + 'float:big' => 'FLOAT', + 'float:normal' => 'FLOAT', + + 'numeric:normal' => 'NUMERIC', + + 'blob:big' => 'BLOB', + 'blob:normal' => 'BLOB', + + 'datetime:normal' => 'TIMESTAMP', + ); + return $map; + } + + /** + * Rename a table. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be renamed. + * @param $new_name + * The new name for the table. + */ + public function renameTable(&$ret, $table, $new_name) { + $ret[] = update_sql('ALTER TABLE {'. $table .'} RENAME TO {'. $new_name .'}'); + } + + /** + * Drop a table. + * + * @param $ret + * Array to which query results will be added. + * @param $table + * The table to be dropped. + */ + public function dropTable(&$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 + */ + public function addField(&$ret, $table, $field, $spec, $keys_new = array()) { + // TODO: keys_new + $query = 'ALTER TABLE {'. $table .'} ADD '; + $query .= _db_create_field_sql($field, _db_processField($spec)); + $ret[] = update_sql($query); + } + + public function dropField(&$ret, $table, $field) { + $schema = drupal_get_schema($table); + unset($schema['fields'][$field]); + foreach ($schema['indexes'] as $index => $fields) { + foreach ($fields as $key => $field_name) { + if ($field_name == $field) { + unset($schema['indexes'][$index][$key]); + } + } + if (empty($schema['indexes'][$index])) { + unset($schema['indexes'][$index]); + } + } + $i = 0; + do { + $new_table = $table . '_' . $i++; + } while ($this->tableExists($new_table)); + $this->createTable($ret, $new_table, $schema); + $ret[] = update_sql('INSERT INTO {'. $new_table .'} SELECT * FROM {'. $table . '}'); + do { + $temp_table = $table . '_' . $i++; + } while ($this->tableExists($temp_table)); + $this->dropTable($ret, $table); + $this->renameTable($ret, $new_table, $table); + } + + public function changeField(&$ret, $table, $field, $field_new, $spec, $keys_new = array()) { + $this->dropField($ret, $table, $field); + $this->addField($ret, $table, $field_new, $spec); + foreach ($keys_new as $name => $fields) { + $this->addIndex($ret, $table, $name, $fields); + } + } + + public function addIndex(&$ret, $table, $name, $fields) { + $ret[] = update_sql($this->_createIndexSql($table, $name, $fields)); + } + + public function dropIndex(&$ret, $table, $name) { + $ret[] = update_sql('DROP INDEX ' . '{' . $table . '}_' . $name); + } + + public function addPrimaryKey(&$ret, $table, $fields) { + } + + public function dropPrimaryKey(&$ret, $table) { + } + + public function addUniqueKey(&$ret, $table, $name, $fields) { + } + + public function dropUniqueKey(&$ret, $table, $name) { + } + + public function fieldSetDefault(&$ret, $table, $field, $default) { + } + + public function fieldSetNoDefault(&$ret, $table, $field) { + } + +} \ No newline at end of file === modified file 'install.php' --- install.php 2008-08-21 19:36:35 +0000 +++ install.php 2008-08-25 08:07:25 +0000 @@ -261,7 +261,6 @@ function install_settings_form(&$form_st '#default_value' => empty($database['username']) ? '' : $database['username'], '#size' => 45, '#maxlength' => 45, - '#required' => TRUE, ); // Database username