? sites/default/modules ? sites/default/settings.php Index: includes/bootstrap.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v retrieving revision 1.252 diff -u -p -r1.252 bootstrap.inc --- includes/bootstrap.inc 22 Nov 2008 16:48:20 -0000 1.252 +++ includes/bootstrap.inc 24 Nov 2008 06:17:54 -0000 @@ -1402,6 +1402,160 @@ function drupal_get_schema($table = NULL } /** + * Get all reserved words for supported database. + * + * @param $database + * The name of database engine. If not given, all reserved words of + * supported database is returned in array format. + * @param $rebuild + * If true, the reserved words will be rebuilt instead of retrieved from the + * cache. + * @return + * Reserved words in array format. + */ +function drupal_get_reserved_words($database = NULL, $rebuild = FALSE) { + static $reserved = array(); + + if (empty($reserved) || $rebuild) { + // Try to load the reserved words from cache. + if (!$rebuild && $cached = cache_get('reserved')) { + $reserved = $cached->data; + } + // Otherwise, rebuild the schema cache. + else { + $reserved = array(); + $drivers = array(); + + foreach (file_scan_directory(DRUPAL_ROOT . '/includes/database', '/^[a-z]*$/i', '/(\.\.?|CVS)$/', 0, FALSE) as $file) { + include_once "{$file->filename}/schema.inc"; + $drivers[$file->basename] = $file->filename; + } + + foreach ($drivers as $driver => $file) { + $class = 'DatabaseSchema_' . $driver; + if (class_exists($class)) { + // Just pass in active connection as dummy object. + $engine = new $class(Database::getActiveConnection('default')); + $reserved[$driver] = $engine->getReservedWords(); + } + } + + // This file contains the reserved words for ANSI standard. + // @link http://developer.mimer.se/validator/sql-reserved-words.tml + /*$reserved['ansi'] = array('absolute', 'action', 'add', 'after', + 'all', 'allocate', 'alter', 'and', 'any', 'are', 'array', 'as', 'asc', + 'asensitive', 'assertion', 'asymmetric', 'at', 'atomic', + 'authorization', 'avg', 'before', 'begin', 'between', 'bigint', + 'binary', 'bit', 'bit_length', 'blob', 'boolean', 'both', 'breadth', + 'by', 'call', 'called', 'cascade', 'cascaded', 'case', 'cast', + 'catalog', 'char', 'char_length', 'character', 'character_length', + 'check', 'clob', 'close', 'coalesce', 'collate', 'collation', + 'column', 'commit', 'condition', 'connect', 'connection', + 'constraint', 'constraints', 'constructor', 'contains', 'continue', + 'convert', 'corresponding', 'count', 'create', 'cross', 'cube', + 'current', 'current_date', 'current_default_transform_group', + 'current_path', 'current_role', 'current_time', 'current_timestamp', + 'current_transform_group_for_type', 'current_user', 'cursor', 'cycle', + 'data', 'date', 'day', 'deallocate', 'dec', 'decimal', 'declare', + 'default', 'deferrable', 'deferred', 'delete', 'depth', 'deref', + 'desc', 'describe', 'descriptor', 'deterministic', 'diagnostics', + 'disconnect', 'distinct', 'do', 'domain', 'double', 'drop', 'dynamic', + 'each', 'element', 'else', 'elseif', 'end', 'equals', 'escape', + 'except', 'exception', 'exec', 'execute', 'exists', 'exit', 'external', + 'extract', 'false', 'fetch', 'filter', 'first', 'float', 'for', + 'foreign', 'found', 'free', 'from', 'full', 'function', 'general', + 'get', 'global', 'go', 'goto', 'grant', 'group', 'grouping', 'handler', + 'having', 'hold', 'hour', 'identity', 'if', 'immediate', 'in', + 'indicator', 'initially', 'inner', 'inout', 'input', 'insensitive', + 'insert', 'int', 'integer', 'intersect', 'interval', 'into', 'is', + 'isolation', 'iterate', 'join', 'key', 'language', 'large', 'last', + 'lateral', 'leading', 'leave', 'left', 'level', 'like', 'local', + 'localtime', 'localtimestamp', 'locator', 'loop', 'lower', 'map', + 'match', 'max', 'member', 'merge', 'method', 'min', 'minute', + 'modifies', 'module', 'month', 'multiset', 'names', 'national', + 'natural', 'nchar', 'nclob', 'new', 'next', 'no', 'none', 'not', + 'null', 'nullif', 'numeric', 'object', 'octet_length', 'of', 'old', + 'on', 'only', 'open', 'option', 'or', 'order', 'ordinality', 'out', + 'outer', 'output', 'over', 'overlaps', 'pad', 'parameter', 'partial', + 'partition', 'path', 'position', 'precision', 'prepare', 'preserve', + 'primary', 'prior', 'privileges', 'procedure', 'public', 'range', + 'read', 'reads', 'real', 'recursive', 'ref', 'references', + 'referencing', 'relative', 'release', 'repeat', 'resignal', 'restrict', + 'result', 'return', 'returns', 'revoke', 'right', 'role', 'rollback', + 'rollup', 'routine', 'row', 'rows', 'savepoint', 'schema', 'scope', + 'scroll', 'search', 'second', 'section', 'select', 'sensitive', + 'session', 'session_user', 'set', 'sets', 'signal', 'similar', 'size', + 'smallint', 'some', 'space', 'specific', 'specifictype', 'sql', + 'sqlcode', 'sqlerror', 'sqlexception', 'sqlstate', 'sqlwarning', + 'start', 'state', 'static', 'submultiset', 'substring', 'sum', + 'symmetric', 'system', 'system_user', 'table', 'tablesample', + 'temporary', 'then', 'time', 'timestamp', 'timezone_hour', + 'timezone_minute', 'to', 'trailing', 'transaction', 'translate', + 'translation', 'treat', 'trigger', 'trim', 'true', 'under', 'undo', + 'union', 'unique', 'unknown', 'unnest', 'until', 'update', 'upper', + 'usage', 'user', 'using', 'value', 'values', 'varchar', 'varying', + 'view', 'when', 'whenever', 'where', 'while', 'window', 'with', + 'within', 'without', 'work', 'write', 'year', 'zone', + );*/ + + // Some well-know reserved words + /*$reserved['well-know'] = array ( + 'user', 'scale', 'type', 'comment', 'view', 'value', 'table', 'index', + 'key', 'sequence', 'trigger', + );*/ + + // This file contains the reserved words for Oracle databases + // @link http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/ap_keywd.htm#i690190 + // @todo: Move to includes/database/oci/schema.inc + $reserved['oci'] = array ( + 'access', 'add', 'all', 'alter', 'and', 'any','as', 'asc', 'audit', + 'between', 'by', 'char', 'check', 'cluster', 'column', 'comment', + 'compress', 'connect', 'create', 'current', 'date', 'decimal', + 'default', 'delete', 'desc', 'distinct', 'drop', 'else', 'exclusive', + 'exists', 'file', 'float', 'for', 'from', 'grant', 'group', 'having', + 'identified', 'immediate', 'in', 'increment', 'index', 'initial', + 'insert', 'integer', 'intersect', 'into', 'is', 'level', 'like', + 'lock', 'long', 'maxextents', 'minus', 'mlslabel', 'mode', 'modify', + 'noaudit', 'nocompress', 'not', 'nowait', 'null', 'number', 'of', + 'offline', 'on', 'online', 'option', 'or', 'order', 'pctfree', 'prior', + 'privileges', 'public', 'raw', 'rename', 'resource', 'revoke', 'row', + 'rowid', 'rownum', 'rows', 'select', 'session', 'set', 'share', 'size', + 'smallint', 'start', 'successful', 'synonym', 'sysdate', 'table', + 'then', 'to', 'trigger', 'uid', 'union', 'unique', 'update', 'user', + 'validate', 'values', 'varchar', 'varchar2', 'view', 'whenever', + 'where', 'with', + ); + + if (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL) { + cache_set('reserved', $reserved); + } + + $reserved['all'] = array(); + foreach ($reserved as $words) { + foreach ($words as $word) { + $reserved['all'][$word] = $word; + } + } + sort($reserved['all']); + + if (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL) { + cache_set('reserved', $reserved); + } + } + } + + if (!isset($database)) { + return $reserved; + } + elseif (isset($reserved[$database])) { + return $reserved[$database]; + } + else { + return FALSE; + } +} + +/** * @} End of "ingroup schemaapi". */ Index: includes/database/schema.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database/schema.inc,v retrieving revision 1.4 diff -u -p -r1.4 schema.inc --- includes/database/schema.inc 31 Oct 2008 15:46:16 -0000 1.4 +++ includes/database/schema.inc 24 Nov 2008 06:17:54 -0000 @@ -423,6 +423,23 @@ abstract class DatabaseSchema { )); return $result->fetchAllKeyed(0, 0); } + + /** + * Returns an array of reserved words (lowercase) for this DB. + * + * You MUST provide the real list for each DB inside every XMLDB class + * + * @return + * Array of reserved words (lowercase) for this DB. + */ + public function getReservedWords() { + // Some well-know reserved words + $reserved_words = array ( + 'user', 'scale', 'type', 'comment', 'view', 'value', 'table', 'index', + 'key', 'sequence', 'trigger', + ); + return $reserved_words; + } } /** Index: includes/database/mysql/schema.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database/mysql/schema.inc,v retrieving revision 1.6 diff -u -p -r1.6 schema.inc --- includes/database/mysql/schema.inc 13 Nov 2008 20:52:13 -0000 1.6 +++ includes/database/mysql/schema.inc 24 Nov 2008 06:17:54 -0000 @@ -310,6 +310,49 @@ class DatabaseSchema_mysql extends Datab $ret[] = update_sql($sql); } + function getReservedWords() { + // This file contains the reserved words for MySQL databases + // @link http://dev.mysql.com/doc/refman/5.0/en/reserved-words.html + $reserved_words = array ( + 'add', 'all', 'alter', 'analyze', 'and', 'as', 'asc', 'asensitive', + 'before', 'between', 'bigint', 'binary', 'blob', 'both', 'by', 'call', + 'cascade', 'case', 'change', 'char', 'character', 'check', 'collate', + 'column', 'condition', 'connection', 'constraint', 'continue', + 'convert', 'create', 'cross', 'current_date', 'current_time', + 'current_timestamp', 'current_user', 'cursor', 'database', 'databases', + 'day_hour', 'day_microsecond', 'day_minute', 'day_second', 'dec', + 'decimal', 'declare', 'default', 'delayed', 'delete', 'desc', + 'describe', 'deterministic', 'distinct', 'distinctrow', 'div', + 'double', 'drop', 'dual', 'each', 'else', 'elseif', 'enclosed', + 'escaped', 'exists', 'exit', 'explain', 'false', 'fetch', 'float', + 'float4', 'float8', 'for', 'force', 'foreign', 'from', 'fulltext', + 'grant', 'group', 'having', 'high_priority', 'hour_microsecond', + 'hour_minute', 'hour_second', 'if', 'ignore', 'in', 'index', 'infile', + 'inner', 'inout', 'insensitive', 'insert', 'int', 'int1', 'int2', + 'int3', 'int4', 'int8', 'integer', 'interval', 'into', 'is', 'iterate', + 'join', 'key', 'keys', 'kill', 'leading', 'leave', 'left', 'like', + 'limit', 'lines', 'load', 'localtime', 'localtimestamp', 'lock', 'long', + 'longblob', 'longtext', 'loop', 'low_priority', 'match', 'mediumblob', + 'mediumint', 'mediumtext', 'middleint', 'minute_microsecond', + 'minute_second', 'mod', 'modifies', 'natural', 'not', + 'no_write_to_binlog', 'null', 'numeric', 'on', 'optimize', 'option', + 'optionally', 'or', 'order', 'out', 'outer', 'outfile', 'precision', + 'primary', 'procedure', 'purge', 'raid0', 'read', 'reads', 'real', + 'references', 'regexp', 'release', 'rename', 'repeat', 'replace', + 'require', 'restrict', 'return', 'revoke', 'right', 'rlike', 'schema', + 'schemas', 'second_microsecond', 'select', 'sensitive', 'separator', + 'set', 'show', 'smallint', 'soname', 'spatial', 'specific', 'sql', + 'sqlexception', 'sqlstate', 'sqlwarning', 'sql_big_result', + 'sql_calc_found_rows', 'sql_small_result', 'ssl', 'starting', + 'straight_join', 'table', 'terminated', 'then', 'tinyblob', 'tinyint', + 'tinytext', 'to', 'trailing', 'trigger', 'true', 'undo', 'union', + 'unique', 'unlock', 'unsigned', 'update', 'upgrade', 'usage', 'use', + 'using', 'utc_date', 'utc_time', 'utc_timestamp', 'values', 'varbinary', + 'varchar', 'varcharacter', 'varying', 'when', 'where', 'while', 'with', + 'write', 'x509', 'xor', 'year_month', 'zerofill', + ); + return $reserved_words; + } } /** Index: includes/database/pgsql/schema.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database/pgsql/schema.inc,v retrieving revision 1.2 diff -u -p -r1.2 schema.inc --- includes/database/pgsql/schema.inc 15 Sep 2008 20:48:07 -0000 1.2 +++ includes/database/pgsql/schema.inc 24 Nov 2008 06:17:54 -0000 @@ -506,4 +506,26 @@ class DatabaseSchema_pgsql extends Datab } } } + + function getReservedWords() { + // This file contains the reserved words for PostgreSQL databases + // @link http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html + $reserved_words = array ( + 'all', 'analyse', 'analyze', 'and', 'any', 'array', 'as', 'asc', + 'asymmetric', 'authorization', 'between', 'binary', 'both', 'case', + 'cast', 'check', 'collate', 'column', 'constraint', 'create', 'cross', + 'current_date', 'current_role', 'current_time', 'current_timestamp', + 'current_user', 'default', 'deferrable', 'desc', 'distinct', 'do', + 'else', 'end', 'except', 'false', 'for', 'foreign', 'freeze', 'from', + 'full', 'grant', 'group', 'having', 'ilike', 'in', 'initially', 'inner', + 'intersect', 'into', 'is', 'isnull', 'join', 'leading', 'left', 'like', + 'limit', 'localtime', 'localtimestamp', 'natural', 'new', 'not', + 'notnull', 'null', 'off', 'offset', 'old', 'on', 'only', 'or', 'order', + 'outer', 'overlaps', 'placing', 'primary', 'references', 'right', + 'select', 'session_user', 'similar', 'some', 'symmetric', 'table', + 'then', 'to', 'trailing', 'true', 'union', 'unique', 'user', 'using', + 'verbose', 'when', 'where', + ); + return $reserved_words; + } } Index: includes/database/sqlite/schema.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database/sqlite/schema.inc,v retrieving revision 1.2 diff -u -p -r1.2 schema.inc --- includes/database/sqlite/schema.inc 23 Nov 2008 21:01:03 -0000 1.2 +++ includes/database/sqlite/schema.inc 24 Nov 2008 06:17:54 -0000 @@ -567,4 +567,30 @@ class DatabaseSchema_sqlite extends Data )); return $result->fetchAllKeyed(0, 0); } + + function getReservedWords() { + // This file contains the reserved words for SQLite databases + // @link http://www.sqlite.org/lang_keywords.html + $reserved_words = array( + 'abort', 'add', 'after', 'all', 'alter', 'analyze', 'and', 'as', 'asc', + 'attach', 'autoincrement', 'before', 'begin', 'between', 'by', + 'cascade', 'case', 'cast', 'check', 'collate', 'column', 'commit', + 'conflict', 'constraint', 'create', 'cross', 'current_date', + 'current_time', 'current_timestamp', 'database', 'default', + 'deferrable', 'deferred', 'delete', 'desc', 'detach', 'distinct', + 'drop', 'each', 'else', 'end', 'escape', 'except', 'exclusive', + 'exists', 'explain', 'fail', 'for', 'foreign', 'from', 'full', 'glob', + 'group', 'having', 'if', 'ignore', 'immediate', 'in', 'index', + 'indexed', 'initially', 'inner', 'insert', 'instead', 'intersect', + 'into', 'is', 'isnull', 'join', 'key', 'left', 'like', 'limit', + 'match', 'natural', 'not', 'notnull', 'null', 'of', 'offset', 'on', + 'or', 'order', 'outer', 'plan', 'pragma', 'primary', 'query', 'raise', + 'references', 'regexp', 'reindex', 'rename', 'replace', 'restrict', + 'right', 'rollback', 'row', 'select', 'set', 'table', 'temp', + 'temporary', 'then', 'to', 'transaction', 'trigger', 'union', 'unique', + 'update', 'using', 'vacuum', 'values', 'view', 'virtual', + 'when', 'where', + ); + return $reserved_words; + } } Index: modules/system/system.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v retrieving revision 1.107 diff -u -p -r1.107 system.admin.inc --- modules/system/system.admin.inc 24 Nov 2008 00:40:45 -0000 1.107 +++ modules/system/system.admin.inc 24 Nov 2008 06:17:54 -0000 @@ -21,6 +21,10 @@ function system_main_admin_page($arg = N if (system_status(TRUE) && user_access('administer site configuration')) { drupal_set_message(t('One or more problems were detected with your Drupal installation. Check the status report for more information.', array('@status' => url('admin/reports/status'))), 'error'); } + // Check for database status report errors. + if (system_database_status(TRUE) && user_access('administer site configuration')) { + drupal_set_message(t('One or more problems were detected with your Drupal database schema. Check the database status report for more information.', array('@database' => url('admin/reports/database'))), 'error'); + } $blocks = array(); if ($admin = db_fetch_array(db_query("SELECT menu_name, mlid FROM {menu_links} WHERE link_path = 'admin' AND module = 'system'"))) { $result = db_query(" @@ -1806,6 +1810,54 @@ function system_status($check = FALSE) { } /** + * Menu callback: displays the database report. Can also be used as a pure check. + * + * @param $check + * If true, only returns a boolean whether there are system status errors. + */ +function system_database_status($check = FALSE) { + global $db_prefix; + $schema = drupal_get_schema(NULL, TRUE); + $reserved = drupal_get_reserved_words(NULL, TRUE); + $reserved_words = implode(', ', $reserved['all']); + unset($reserved['all']); + + $conflict = array(); + $table_conflict = FALSE; + $column_conflict = FALSE; + foreach ($reserved as $database => $words) { + foreach ($schema as $table_name => $table) { + if (array_search($table_name, $words)) { + $conflict[$table_name][] = $database; + $table_conflict = TRUE; + } + foreach ($table['fields'] as $column_name => $column) { + if (array_search($column_name, $words)) { + $conflict[$table_name . '.' . $column_name][] = $database; + $column_conflict = TRUE; + } + } + } + } + ksort($conflict); + foreach ($conflict as &$database) { + $database = implode(', ', $database); + } + + if ($check) { + if (empty($db_prefix) && $table_conflict) { + return TRUE; + } + elseif ($column_conflict) { + return TRUE; + } + return FALSE; + } + + return theme('database_status_report', $reserved_words, $conflict); +} + +/** * Menu callback: run cron manually. */ function system_run_cron() { @@ -2093,6 +2145,35 @@ function theme_status_report(&$requireme } /** + * Theme schema report. + * + * @param $conflict + * An array of conflict reserved words. + * @ingroup themeable + */ +function theme_database_status_report($reserved_words, $conflict) { + $output = ''; + + if (count($conflict > 0)) { + $output .= "\n

" . t('Currently Used Reserved Words') . "

\n"; + $output .= "

" . t("(note that table names aren't important if using table prefix)") . "

\n"; + + $i = 0; + $output .= ''; + foreach ($conflict as $word => $databases) { + $class = ++$i % 2 == 0 ? 'even' : 'odd'; + $output .= ''; + } + $output .= '
' . $word . '' . $databases . '
'; + } + + $output .= "\n

" . t('List of Reserved Words') . "

\n"; + $output .= "

" . $reserved_words . "

"; + + return $output; +} + +/** * Theme callback for the modules form. * * @param $form Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.645 diff -u -p -r1.645 system.module --- modules/system/system.module 24 Nov 2008 00:40:45 -0000 1.645 +++ modules/system/system.module 24 Nov 2008 06:17:54 -0000 @@ -111,6 +111,8 @@ function system_help($path, $arg) { return '

' . t('IP addresses listed here are blocked from your site before any modules are loaded. You may add IP addresses to the list, or delete existing entries.') . '

'; case 'admin/reports/status': return '

' . t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation. It may be useful to copy and paste this information into support requests filed on drupal.org's support forums and project issue queues.") . '

'; + case 'admin/reports/database': + return '

' . t("Here you can find a short overview of your database schema as well as any problems detected with your installation. It may be useful to copy and paste this information into support requests filed on drupal.org's support forums and project issue queues.") . '

'; } } @@ -143,6 +145,10 @@ function system_theme() { 'arguments' => array('requirements' => NULL), 'file' => 'system.admin.inc', ), + 'database_status_report' => array( + 'arguments' => array('reserved_words' => NULL, 'conflict' => NULL), + 'file' => 'system.admin.inc', + ), 'admin_page' => array( 'arguments' => array('blocks' => NULL), 'file' => 'system.admin.inc', @@ -688,6 +694,13 @@ function system_menu() { 'weight' => 10, 'access arguments' => array('administer site configuration'), ); + $items['admin/reports/database'] = array( + 'title' => 'Database schema report', + 'description' => "Get a report about your database schema and any detected problems.", + 'page callback' => 'system_database_status', + 'weight' => 15, + 'access arguments' => array('administer site configuration'), + ); $items['admin/reports/status/run-cron'] = array( 'title' => 'Run cron', 'page callback' => 'system_run_cron',