Browse files

Working on handling table indexes when running install or update comm…

…ands on CLI. Still unstable.
  • Loading branch information...
1 parent c3bbed3 commit 0712516e7012810e53510ac9d983d5527bac4003 @geekforbrains committed Mar 15, 2012
Showing with 135 additions and 57 deletions.
  1. +13 −2 core/db/controllers/db.php
  2. +5 −2 core/db/db.php
  3. +35 −22 core/db/db_query.php
  4. +82 −31 core/db/db_runner.php
View
15 core/db/controllers/db.php
@@ -3,7 +3,14 @@
class Db_DbController extends Controller {
/**
- * TODO
+ * Allows database install, update, seed and optimize commands to be run
+ * through the URL instead of forcing people to use the CLI.
+ *
+ * The config "db.enabled_url_runner" must be set to "true" to allow running
+ * commands otherwise 404 will be returned.
+ *
+ * The "db.enable_url_runner" command should be set to "false" in a production
+ * environment.
*/
public static function runner($cmd, $force = false)
{
@@ -25,8 +32,12 @@ public static function runner($cmd, $force = false)
Db_Runner::seed();
break;
+ case 'optimize':
+ Db_Runner::optimize();
+ break;
+
default:
- die('Invalid command, must be install, update or seed.');
+ die('Invalid command. Must be install, update ,seed or optimize.');
}
exit();
View
7 core/db/db.php
@@ -22,7 +22,10 @@ public static function query($sql, $bindings = array(), $getInsertId = true, $cl
$debugSql = $sql;
foreach($bindings as $value)
$debugSql = preg_replace('/\?/', "'".$value."'", $debugSql, 1);
- Dev::debug('db', $debugSql);
+
+ // Show and Describe queries are too noisy, ignore them
+ if(!strstr($debugSql, 'SHOW') && !strstr($debugSql, 'DESCRIBE'))
+ Dev::debug('db', $debugSql);
$query = self::$_conn->prepare($sql);
$result = $query->execute($bindings);
@@ -34,7 +37,7 @@ public static function query($sql, $bindings = array(), $getInsertId = true, $cl
}
else
{
- if(stripos($sql, 'SHOW TABLES') === 0)
+ if(stripos($sql, 'SHOW') === 0)
return $query->fetchAll(); // Always return show tables as an array
elseif(stripos($sql, 'SELECT') === 0 || stripos($sql, 'DESCRIBE') === 0)
View
57 core/db/db_query.php
@@ -2,8 +2,8 @@
class Db_Query extends Module {
- public $tableName = null;
- public $fieldNames = array();
+ //public $tableName = null;
+ //public $fieldNames = array();
private $_table = null;
private $_class = null;
@@ -28,7 +28,8 @@ class Db_Query extends Module {
protected $_belongsTo = array();
protected $_hasAndBelongsToMany = array();
- private static $_describes = array();
+ private static $_describes = array();
+ private static $_exists = array();
/**
* Allow read access to private properties.
@@ -43,18 +44,19 @@ public function __get($name) {
public function __construct($table = null)
{
if(!is_null($table))
- $this->tableName = $table;
+ $this->_table = $table;
- if(is_null($this->tableName))
- $this->tableName = $this->_getTableName();
+ if(is_null($this->_table))
+ $this->_table = $this->_getTableName();
- $this->_table = $this->tableName;
+ //$this->_table = $this->tableName;
$this->_from = sprintf(' FROM %s', $this->_table);
// Produce query results that are of the same class as this model
$this->_class = get_called_class();
// Setup blank properties for this class based on table description
+ /*
$fields = $this->describe();
if($fields && is_array($fields))
{
@@ -65,6 +67,7 @@ public function __construct($table = null)
$this->{$field->Field} = null;
}
}
+ */
}
/**
@@ -194,25 +197,36 @@ public function describe()
{
if(!isset(self::$_describes[$this->_table]))
{
- $data = Db::query('DESCRIBE ' . $this->_table);
- // Only store a describe if it returns data, otherwise the table probably wasn't created yet
- if($data)
- self::$_describes[$this->_table] = $data;
+ if($this->exists())
+ {
+ $data = Db::query('DESCRIBE ' . $this->_table);
+
+ // Only store a describe if it returns data, otherwise the table probably wasn't created yet
+ if($data)
+ self::$_describes[$this->_table] = $data;
+ else
+ return $data;
+ }
else
- return $data;
+ return null;
}
return self::$_describes[$this->_table];
}
/**
- * ---------------------------------------------------------------------------
- * TODO
- * ---------------------------------------------------------------------------
+ * Determines if a table exists. After this first call to this, the response is stored
+ * to avoid multiple, unecessary calls to the database.
+ *
+ * @param boolean $force Forces the query to be run, regardless if its been run before.
*/
- public function exists() {
- return Db::query('SHOW TABLES LIKE \'' . $this->_table . '\'');
+ public function exists($force = false)
+ {
+ if(!isset(self::$_exists[$this->_table]) || $force)
+ self::$_exists[$this->_table] = Db::query('SHOW TABLES LIKE \'' . $this->_table . '\'');
+
+ return self::$_exists[$this->_table];
}
@@ -421,11 +435,11 @@ public function leftJoin($table, $column1, $operator, $column2)
return $this->join($table, $column1, $operator, $column2, 'LEFT');
}
-
/**
- * ---------------------------------------------------------------------------
- * TODO
- * ---------------------------------------------------------------------------
+ * Determines the tables name based on the class name. The table name will be
+ * the module name, an underscore and the model name in plural form.
+ *
+ * Example: Blog_PostModel = blog_posts
*/
private function _getTableName()
{
@@ -439,5 +453,4 @@ private function _getTableName()
return String::plural($model);
}
-
}
View
113 core/db/db_runner.php
@@ -13,11 +13,12 @@ class Db_Runner extends Db_Driver {
*/
public static function install($force = false)
{
- self::_clearDatabase($force);
-
- if($models = self::_getModels())
- foreach($models as $model)
- self::_createTable(new $model());
+ if(self::_clearDatabase($force))
+ {
+ if($models = self::_getModels())
+ foreach($models as $model)
+ self::_createTable(new $model());
+ }
}
/**
@@ -151,16 +152,18 @@ private static function _createTable($model)
));
}
- $sql = 'CREATE TABLE ' . $model->tableName . ' (';
+ //$sql = 'CREATE TABLE IF NOT EXISTS ' . $model->tableName . ' (';
+ $sql = 'CREATE TABLE IF NOT EXISTS ' . $model->_table . ' (';
foreach($fields as $field => $fieldData)
$sql .= self::_buildField($field, $fieldData);
foreach($model->_indexes as $k)
$sql .= 'INDEX(' . $k . '),';
- if($model->_fulltext)
- $sql .= sprintf('FULLTEXT(%s),', implode(',', $model->_fulltext));
+ foreach($model->_fulltext as $ft)
+ $sql .= sprintf('FULLTEXT(' . $ft . '),');
+ //$sql .= sprintf('FULLTEXT(%s),', implode(',', $model->_fulltext));
if(isset($fields['id']))
$sql .= 'PRIMARY KEY(id)';
@@ -205,7 +208,8 @@ private static function _createHABTM($model)
{
foreach($model->_hasAndBelongsToMany as $refModel)
{
- $tbl1Bits = explode('_', $model->tableName);
+ //$tbl1Bits = explode('_', $model->tableName);
+ $tbl1Bits = explode('_', $model->_table);
$tbl1Key = $tbl1Bits[0] . '_' . String::singular($tbl1Bits[1]);
$tbl1 = implode('', $tbl1Bits);
@@ -231,17 +235,32 @@ private static function _createHABTM($model)
/**
* Gets the details of a models table and compares it with the current fields and options
* of the model. If anything has changed, the table will be altered
+ *
+ * TODO Create belongsTo fields and indexes that are new/re-added
*/
private static function _updateTable($model)
{
$rawFields = $model->describe();
- //print_r($rawFields);
-
$tableFields = array();
+ $belongsToFields = array();
+
+ foreach($model->_belongsTo as $ref)
+ {
+ $refBits = explode('.', $ref);
+ $belongsToFields[] = $refBits[1] . '_id';
+ }
// Sort table fields into nice array format for easier look ups by id and type
foreach($rawFields as $f)
{
+ // Ignore timestamps fields, we'll handle those seperately
+ if($f->Field == 'created_at' || $f->Field == 'updated_at')
+ continue;
+
+ // Ignore any belongsTo ids (foreign keys)
+ if($model->_belongsTo && in_array($f->Field, $belongsToFields))
+ continue;
+
$tableFields[$f->Field] = array(
'type' => $f->Type,
'not null' => ($f->Null == 'NO') ? true : false,
@@ -263,16 +282,13 @@ private static function _updateTable($model)
Db::query(rtrim('ALTER TABLE ' . $model->_table . ' ADD ' . self::_buildField($field, $data), ','));
// Check if model field is different from current able field, if it is, update
- else
- {
- if(self::_fieldIsDifferent($field, $data, $tableFields[$field]))
- Db::query(rtrim('ALTER TABLE ' . $model->_table . ' MODIFY ' . self::_buildField($field, $data), ','));
-
- // Check for new or removed indexes
- self::_checkIndexes($model->_table, $field, $data, $tableFields[$field], $model->_indexes);
- }
+ elseif(self::_fieldIsDifferent($field, $data, $tableFields[$field]))
+ Db::query(rtrim('ALTER TABLE ' . $model->_table . ' MODIFY ' . self::_buildField($field, $data), ','));
}
+ // Check for new or removed indexes
+ self::_checkIndexes($model, $belongsToFields);
+
// Check for fields that were removed from model, but exist in table, delete them
// TODO Add warning with option to skip, delete or delete without prompt
// TODO Above should work with http mode via force, or cmd line
@@ -281,8 +297,6 @@ private static function _updateTable($model)
if(!isset($modelFields[$field]))
Db::query('ALTER TABLE ' . $model->_table . ' DROP COLUMN ' . $field);
}
-
- exit();
}
/**
@@ -356,7 +370,7 @@ private static function _clearDatabase($force = false)
if($answer == 'yes')
break;
elseif($answer == 'no')
- return;
+ return false;
else
fwrite(STDOUT, "Please enter yes or no\n");
}
@@ -368,6 +382,8 @@ private static function _clearDatabase($force = false)
foreach($results as $table)
Db::query('DROP TABLE ' . $dbName . '.' . $table[0]);
}
+
+ return true;
}
/**
@@ -429,18 +445,53 @@ private static function _fieldIsDifferent($field, $modelData, $tableData)
* but not in the table, a new index is added. If it was removed from the model but exists in the table
* the index will be removed.
*/
- private static function _checkIndexes($table, $field, $modelData, $tableData, $indexes)
+ private static function _checkIndexes($model, $belongsToFields)
{
- $modelData['key'] = false;
+ if($result = Db::query('SHOW INDEX FROM ' . $model->_table))
+ {
+ $tableIndexes = array();
- if(in_array($field, $indexes) || $modelData['type'] == 'auto increment')
- $modelData['key'] = true;
-
- if($modelData['key'] && !$tableData['key'])
- Db::query('CREATE INDEX ' . $field . ' ON ' . $table . ' (' . $field . ')');
+ foreach($result as $row)
+ {
+ if($row['Key_name'] == 'PRIMARY')
+ continue;
+
+ $tableIndexes[$row['Key_name']] = $row['Index_type'];
+ }
+
+ // Check for indexes in model, that aren't in table
+ foreach($model->_indexes as $field)
+ {
+ if(!isset($tableIndexes[$field]))
+ Db::query('CREATE INDEX ' . $field . ' ON ' . $model->_table . ' (' . $field . ')');
+ }
+
+ // Check for fulltext indexes in model, that aren't in table
+ foreach($model->_fulltext as $field)
+ {
+ if(!isset($tableIndexes[$field]))
+ Db::query('ALTER TABLE ' . $model->_table . ' ADD FULLTEXT(' . $field . ')');
+ }
- elseif(!$modelData['key'] && $tableData['key'])
- Db::query('ALTER TABLE ' . $table . ' DROP INDEX ' . $field);
+ // Check for indexes in table, that arent in model, and drop them
+ foreach($tableIndexes as $field => $indexType)
+ {
+ // Ignore indexes created from belongsTo
+ if(in_array($field, $belongsToFields))
+ continue;
+
+ if(($indexType == 'BTREE' && !in_array($field, $model->_indexes)) ||
+ ($indexType == 'FULLTEXT' && !in_array($field, $model->_fulltext)))
+ Db::query('ALTER TABLE ' . $model->_table . ' DROP INDEX ' . $field);
+ }
+
+ // Check for belongsTo fields that aren't in the table, and create them
+ foreach($belongsToFields as $field)
+ {
+ if(!isset($tableIndexes[$field]))
+ Db::query('CREATE INDEX ' . $field . ' ON ' . $model->_table . ' (' . $field . ')');
+ }
+ }
}
}

0 comments on commit 0712516

Please sign in to comment.