Skip to content

Commit

Permalink
Completed ticket #565 - fixed fSchema to be more robust in handling S…
Browse files Browse the repository at this point in the history
…QLite databases, including comments in `CREATE TABLE` statements and `UNIQUE` indexes created after table creation
  • Loading branch information
wbond committed Apr 12, 2012
1 parent 0dd2093 commit 04957f7
Showing 1 changed file with 41 additions and 8 deletions.
49 changes: 41 additions & 8 deletions classes/fSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
/**
* Gets schema information for the selected database
*
* @copyright Copyright (c) 2007-2010 Will Bond
* @copyright Copyright (c) 2007-2011 Will Bond
* @author Will Bond [wb] <[email protected]>
* @license http://flourishlib.com/license
*
* @package Flourish
* @link http://flourishlib.com/fSchema
*
* @version 1.0.0b45
* @version 1.0.0b46
* @changes 1.0.0b46 Enhanced SQLite schema detection to cover situations where `UNIQUE` constraints are defined separately from the table and when comments are used in `CREATE TABLE` statements [wb, 2011-02-06]
* @changes 1.0.0b45 Fixed Oracle auto incrementing detection to work with `INSERT OR UPDATE` triggers, fixed detection of dynamic default date/time/timestamp values for DB2 and Oracle [wb, 2010-12-04]
* @changes 1.0.0b44 Fixed the list of valid elements for ::getColumnInfo() [wb, 2010-11-28]
* @changes 1.0.0b43 Added the `comment` element to the information returned by ::getColumnInfo() [wb, 2010-11-28]
Expand Down Expand Up @@ -1888,7 +1889,7 @@ private function fetchSQLiteColumnInfo($table)
return array();
}

preg_match_all('#(?<=,|\()\s*(?:`|"|\[)?(\w+)(?:`|"|\])?\s+([a-z]+)(?:\(\s*(\d+)(?:\s*,\s*(\d+))?\s*\))?(?:(\s+NOT\s+NULL)|(?:\s+NULL)|(?:\s+DEFAULT\s+([^, \']*|\'(?:\'\'|[^\']+)*\'))|(\s+UNIQUE)|(\s+PRIMARY\s+KEY(?:\s+AUTOINCREMENT)?)|(\s+CHECK\s*\(\w+\s+IN\s+\(\s*(?:(?:[^, \']+|\'(?:\'\'|[^\']+)*\')\s*,\s*)*\s*(?:[^, \']+|\'(?:\'\'|[^\']+)*\')\)\)))*(\s+REFERENCES\s+["`\[]?\w+["`\]]?\s*\(\s*["`\[]?\w+["`\]]?\s*\)\s*(?:\s+(?:ON\s+DELETE|ON\s+UPDATE)\s+(?:CASCADE|NO\s+ACTION|RESTRICT|SET\s+NULL|SET\s+DEFAULT))*(?:\s+(?:DEFERRABLE|NOT\s+DEFERRABLE))?)?([ \t]*--[^\n]+)?\s*(?:,([ \t]*--[^\n]+)?|\s*(?=\)))#mi', $create_sql, $matches, PREG_SET_ORDER);
preg_match_all('#(?<=,|\(|\*/|\n)\s*(?:`|"|\[)?(\w+)(?:`|"|\])?\s+([a-z]+)(?:\(\s*(\d+)(?:\s*,\s*(\d+))?\s*\))?(?:(\s+NOT\s+NULL)|(?:\s+NULL)|(?:\s+DEFAULT\s+([^, \']*|\'(?:\'\'|[^\']+)*\'))|(\s+UNIQUE)|(\s+PRIMARY\s+KEY(?:\s+AUTOINCREMENT)?)|(\s+CHECK\s*\(\w+\s+IN\s+\(\s*(?:(?:[^, \']+|\'(?:\'\'|[^\']+)*\')\s*,\s*)*\s*(?:[^, \']+|\'(?:\'\'|[^\']+)*\')\)\)))*(\s+REFERENCES\s+["`\[]?\w+["`\]]?\s*\(\s*["`\[]?\w+["`\]]?\s*\)\s*(?:\s+(?:ON\s+DELETE|ON\s+UPDATE)\s+(?:CASCADE|NO\s+ACTION|RESTRICT|SET\s+NULL|SET\s+DEFAULT))*(?:\s+(?:DEFERRABLE|NOT\s+DEFERRABLE))?)?([ \t]*(?:/\*(?:(?!\*/).)*\*/))?\s*(?:,([ \t]*--[^\n]*\n)?|(--[^\n]*\n)?\s*(?=\)))#msi', $create_sql, $matches, PREG_SET_ORDER);

foreach ($matches as $match) {
$info = array();
Expand Down Expand Up @@ -1945,9 +1946,17 @@ private function fetchSQLiteColumnInfo($table)
}

// Column comments
if (!empty($match[11]) || !empty($match[12])) {
$comment = empty($match[12]) ? $match[11] : $match[12];
$info['comment'] = ltrim(substr(trim($comment), 2));
if (!empty($match[11]) || !empty($match[12]) || !empty($match[13])) {
if (!empty($match[11])) {
$comment = $match[11];
} elseif (!empty($match[12])) {
$comment = $match[12];
} else {
$comment = $match[13];
}
$comment = trim($comment);
$comment = substr($comment, 0, 2) == '--' ? substr($comment, 2) : substr($comment, 2, -2);
$info['comment'] = trim($comment);
}

$column_info[$match[1]] = $info;
Expand Down Expand Up @@ -1977,6 +1986,15 @@ private function fetchSQLiteKeys()
$row = $result->fetchRow();
$create_sql = $row['sql'];

// Collapse strings into empty string to make the matching simpler
$create_sql = preg_replace('#\'(?:\'\'|[^\']+)*\'#', "''", $create_sql);

// Remove single-line comments
$create_sql = preg_replace('#--[^\n]*\n#', "\n", $create_sql);

// Remove multi-line comments
$create_sql = preg_replace('#/\*((?!\*/).)*\*/#', '', $create_sql);

// Get column level key definitions
preg_match_all('#(?<=,|\()\s*["`\[]?(\w+)["`\]]?\s+(?:[a-z]+)(?:\((?:\d+)\))?(?:(?:\s+NOT\s+NULL)|(?:\s+DEFAULT\s+(?:[^, \']*|\'(?:\'\'|[^\']+)*\'))|(\s+UNIQUE)|(\s+PRIMARY\s+KEY(?:\s+AUTOINCREMENT)?)|(?:\s+CHECK\s*\(\w+\s+IN\s+\(\s*(?:(?:[^, \']+|\'(?:\'\'|[^\']+)*\')\s*,\s*)*\s*(?:[^, \']+|\'(?:\'\'|[^\']+)*\')\)\)))*(\s+REFERENCES\s+["`\[]?(\w+)["`\]]?\s*\(\s*["`\[]?(\w+)["`\]]?\s*\)\s*(?:(?:\s+(?:ON\s+DELETE\s+(CASCADE|NO\s+ACTION|RESTRICT|SET\s+NULL|SET\s+DEFAULT)))|(?:\s+(?:ON\s+UPDATE\s+(CASCADE|NO\s+ACTION|RESTRICT|SET\s+NULL|SET\s+DEFAULT))))*(?:\s+(?:DEFERRABLE|NOT\s+DEFERRABLE))?)?\s*(?:,|\s*(?=\)))#mi', $create_sql, $matches, PREG_SET_ORDER);

Expand Down Expand Up @@ -2016,7 +2034,7 @@ private function fetchSQLiteKeys()
}

// Get table level foreign key definitions
preg_match_all('#(?<=,|\()\s*FOREIGN\s+KEY\s*(?:["`\[]?(\w+)["`\]]?|\(\s*["`\[]?(\w+)["`\]]?\s*\))\s+REFERENCES\s+["`\[]?(\w+)["`\]]?\s*\(\s*["`\[]?(\w+)["`\]]?\s*\)\s*(?:\s+(?:ON\s+DELETE\s+(CASCADE|NO\s+ACTION|RESTRICT|SET\s+NULL|SET\s+DEFAULT)))?(?:\s+(?:ON\s+UPDATE\s+(CASCADE|NO\s+ACTION|RESTRICT|SET\s+NULL|SET\s+DEFAULT)))?(?:\s+(?:DEFERRABLE|NOT\s+DEFERRABLE))?\s*(?:,|\s*(?=\)))#mi', $create_sql, $matches, PREG_SET_ORDER);
preg_match_all('#(?<=,|\()\s*FOREIGN\s+KEY\s*(?:["`\[]?(\w+)["`\]]?|\(\s*["`\[]?(\w+)["`\]]?\s*\))\s+REFERENCES\s+["`\[]?(\w+)["`\]]?\s*\(\s*["`\[]?(\w+)["`\]]?\s*\)\s*(?:\s+(?:ON\s+DELETE\s+(CASCADE|NO\s+ACTION|RESTRICT|SET\s+NULL|SET\s+DEFAULT)))?(?:\s+(?:ON\s+UPDATE\s+(CASCADE|NO\s+ACTION|RESTRICT|SET\s+NULL|SET\s+DEFAULT)))?(?:\s+(?:DEFERRABLE|NOT\s+DEFERRABLE))?\s*(?:,|\s*(?=\)))#mis', $create_sql, $matches, PREG_SET_ORDER);

foreach ($matches as $match) {
if (empty($match[1])) { $match[1] = $match[2]; }
Expand Down Expand Up @@ -2045,6 +2063,21 @@ private function fetchSQLiteKeys()
}
$keys[$table]['unique'][] = $key;
}

// Get all CREATE UNIQUE INDEX statements
$result = $this->database->query("SELECT sql FROM sqlite_master WHERE type = 'index' AND sql <> '' AND tbl_name = %s", $table);
foreach ($result as $row) {
$create_sql = $row['sql'];
if (!preg_match('#^\s*CREATE\s+UNIQUE\s+INDEX\s+(?:\w+\.)?\w+\s+ON\s+\w+\s*\(\s*((?:\s*["`\[]?\w+["`\]]?\s*,\s*)*["`\[]?\w+["`\]]?)\s*\)$#Di', $create_sql, $match)) {
continue;
}
$columns = preg_split('#\s*,\s*#', $match[1]);
$key = array();
foreach ($columns as $column) {
$key[] = str_replace(array('[', '"', '`', ']'), '', $column);
}
$keys[$table]['unique'][] = $key;
}
}

return $keys;
Expand Down Expand Up @@ -2972,7 +3005,7 @@ public function setKeysOverride($keys, $table, $key_type=NULL)


/**
* Copyright (c) 2007-2010 Will Bond <[email protected]>
* Copyright (c) 2007-2011 Will Bond <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down

0 comments on commit 04957f7

Please sign in to comment.