From 6bed503b567a3e2ea85ac913d31f320b346b7b4f Mon Sep 17 00:00:00 2001 From: nate Date: Sun, 18 May 2008 06:42:36 +0000 Subject: [PATCH] Refactoring schema handling for SQLite and Postgres drivers. Non-primary key indexes are now created properly. Fixed testing bugs in db drivers. git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@6922 3807eeeb-6ff5-0310-8944-8be069107fe0 --- .../model/datasources/dbo/dbo_postgres.php | 56 +++++++++--- .../libs/model/datasources/dbo/dbo_sqlite.php | 85 ++++++++++++++++++- cake/libs/model/datasources/dbo_source.php | 43 +++++++--- .../model/datasources/dbo/dbo_mysql.test.php | 4 +- cake/tests/cases/libs/model/model.test.php | 13 +-- 5 files changed, 166 insertions(+), 35 deletions(-) diff --git a/cake/libs/model/datasources/dbo/dbo_postgres.php b/cake/libs/model/datasources/dbo/dbo_postgres.php index 586a677b4..8047cf8c7 100644 --- a/cake/libs/model/datasources/dbo/dbo_postgres.php +++ b/cake/libs/model/datasources/dbo/dbo_postgres.php @@ -597,28 +597,62 @@ class DboPostgres extends DboSource { * Format indexes for create table * * @param array $indexes + * @param string $table * @return string */ - function buildIndex($indexes) { + function buildIndex($indexes, $table = null) { $join = array(); foreach ($indexes as $name => $value) { - $out = ''; + if ($name == 'PRIMARY') { - $out .= 'PRIMARY '; - $name = null; + $out = 'PRIMARY KEY (' . $this->name($value['column']) . ')'; } else { + $out = 'CREATE '; if (!empty($value['unique'])) { - $name .= ' UNIQUE '; + $out .= 'UNIQUE '; } - } - if (is_array($value['column'])) { - $out .= 'CONSTRAINT '. $name .' (' . join(', ', array_map(array(&$this, 'name'), $value['column'])) . ')'; - } else { - $out .= 'KEY '. $name .' (' . $this->name($value['column']) . ')'; + if (is_array($value['column'])) { + $value['column'] = join(', ', array_map(array(&$this, 'name'), $value['column'])); + } else { + $value['column'] = $this->name($value['column']); + } + $out .= "INDEX {$name} ON {$table}({$value['column']});"; } $join[] = $out; } - return join(",\n\t", $join); + return $join; + } +/** + * Overrides DboSource::renderStatement to handle schema generation with Postgres-style indexes + * + * @param string $type + * @param array $data + * @return string + */ + function renderStatement($type, $data) { + switch (strtolower($type)) { + case 'schema': + extract($data); + + foreach ($indexes as $i => $index) { + if (preg_match('/PRIMARY KEY/', $index)) { + unset($indexes[$i]); + $columns[] = $index; + break; + } + } + + foreach (array('columns', 'indexes') as $var) { + if (is_array(${$var})) { + ${$var} = "\t" . join(",\n\t", array_filter(${$var})); + } + } + return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}"; + break; + default: + return parent::renderStatement($type, $data); + break; + } } } diff --git a/cake/libs/model/datasources/dbo/dbo_sqlite.php b/cake/libs/model/datasources/dbo/dbo_sqlite.php index d7128f001..333103df9 100644 --- a/cake/libs/model/datasources/dbo/dbo_sqlite.php +++ b/cake/libs/model/datasources/dbo/dbo_sqlite.php @@ -93,6 +93,10 @@ class DboSqlite extends DboSource { $config = $this->config; $this->connection = $config['connect']($config['database']); $this->connected = is_resource($this->connection); + + if ($this->connected) { + $this->_execute('PRAGMA count_changes = 1'); + } return $this->connected; } /** @@ -186,7 +190,7 @@ class DboSqlite extends DboSource { * @param string $data String to be prepared for use in an SQL statement * @return string Quoted and escaped */ - function value ($data, $column = null, $safe = false) { + function value($data, $column = null, $safe = false) { $parent = parent::value($data, $column, $safe); if ($parent != null) { @@ -237,7 +241,7 @@ class DboSqlite extends DboSource { * @return boolean True on success, false on fail * (i.e. if the database/model does not support transactions). */ - function begin (&$model) { + function begin(&$model) { if (parent::begin($model)) { if ($this->execute('BEGIN')) { $this->_transactionStarted = true; @@ -254,7 +258,7 @@ class DboSqlite extends DboSource { * (i.e. if the database/model does not support transactions, * or a transaction has not started). */ - function commit (&$model) { + function commit(&$model) { if (parent::commit($model)) { $this->_transactionStarted = false; return $this->execute('COMMIT'); @@ -269,7 +273,7 @@ class DboSqlite extends DboSource { * (i.e. if the database/model does not support transactions, * or a transaction has not started). */ - function rollback (&$model) { + function rollback(&$model) { if (parent::rollback($model)) { return $this->execute('ROLLBACK'); } @@ -485,6 +489,79 @@ class DboSqlite extends DboSource { } return $out; } +/** + * Sets the database encoding + * + * @param string $enc Database encoding + */ + function setEncoding($enc) { + if (!in_array($enc, array("UTF-8", "UTF-16", "UTF-16le", "UTF-16be"))) { + return false; + } + return $this->_execute("PRAGMA encoding = \"{$enc}\"") !== false; + } +/** + * Gets the database encoding + * + * @return string The database encoding + */ + function getEncoding() { + return $this->fetchRow('PRAGMA encoding'); + } +/** + * Removes redundant primary key indexes, as they are handled in the column def of the key. + * + * @param array $indexes + * @param string $table + * @return string + */ + function buildIndex($indexes, $table = null) { + $join = array(); + + foreach ($indexes as $name => $value) { + + if ($name == 'PRIMARY') { + continue; + } + $out = 'CREATE '; + + if (!empty($value['unique'])) { + $out .= 'UNIQUE '; + } + if (is_array($value['column'])) { + $value['column'] = join(', ', array_map(array(&$this, 'name'), $value['column'])); + } else { + $value['column'] = $this->name($value['column']); + } + $out .= "INDEX {$name} ON {$table}({$value['column']});"; + $join[] = $out; + } + return $join; + } +/** + * Overrides DboSource::renderStatement to handle schema generation with SQLite-style indexes + * + * @param string $type + * @param array $data + * @return string + */ + function renderStatement($type, $data) { + switch (strtolower($type)) { + case 'schema': + extract($data); + + foreach (array('columns', 'indexes') as $var) { + if (is_array(${$var})) { + ${$var} = "\t" . join(",\n\t", array_filter(${$var})); + } + } + return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}"; + break; + default: + return parent::renderStatement($type, $data); + break; + } + } } ?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo_source.php b/cake/libs/model/datasources/dbo_source.php index bd27b2aa0..222f758d5 100644 --- a/cake/libs/model/datasources/dbo_source.php +++ b/cake/libs/model/datasources/dbo_source.php @@ -1232,6 +1232,19 @@ class DboSource extends DataSource { } return "DELETE {$alias} FROM {$table} {$aliases}{$conditions}"; break; + case 'schema': + foreach (array('columns', 'indexes') as $var) { + if (is_array(${$var})) { + ${$var} = "\t" . join(",\n\t", array_filter(${$var})); + } + } + if (trim($indexes) != '') { + $columns .= ','; + } + return "CREATE TABLE {$table} (\n{$columns}{$indexes});"; + break; + case 'alter': + break; } } /** @@ -2007,11 +2020,11 @@ class DboSource extends DataSource { * Generate a database-native schema for the given Schema object * * @param object $schema An instance of a subclass of CakeSchema - * @param string $table Optional. If specified only the table name given will be generated. + * @param string $tableName Optional. If specified only the table name given will be generated. * Otherwise, all tables defined in the schema are generated. * @return string */ - function createSchema($schema, $table = null) { + function createSchema($schema, $tableName = null) { if (!is_a($schema, 'CakeSchema')) { trigger_error(__('Invalid schema object', true), E_USER_WARNING); return null; @@ -2019,10 +2032,11 @@ class DboSource extends DataSource { $out = ''; foreach ($schema->tables as $curTable => $columns) { - if (!$table || $table == $curTable) { - $out .= 'CREATE TABLE ' . $this->fullTableName($curTable) . " (\n"; - $cols = $colList = $index = array(); + if (!$tableName || $tableName == $curTable) { + $cols = $colList = $indexes = array(); $primary = null; + $table = $this->fullTableName($curTable); + foreach ($columns as $name => $col) { if (is_string($col)) { $col = array('type' => $col); @@ -2037,14 +2051,15 @@ class DboSource extends DataSource { } $cols[] = $this->buildColumn($col); } else { - $index[] = $this->buildIndex($col); + $indexes = array_merge($indexes, $this->buildIndex($col, $table)); } } - if (empty($index) && !empty($primary)) { - $col = array('PRIMARY' => array('column'=> $primary, 'unique' => 1)); - $index[] = $this->buildIndex($col); + if (empty($indexes) && !empty($primary)) { + $col = array('PRIMARY' => array('column' => $primary, 'unique' => 1)); + $indexes = array_merge($indexes, $this->buildIndex($col, $table)); } - $out .= "\t" . join(",\n\t", array_filter(array_merge($cols, $index))) . "\n);\n\n"; + $columns = $cols; + $out .= $this->renderStatement('schema', compact('table', 'columns', 'indexes')) . "\n\n"; } } return $out; @@ -2135,9 +2150,10 @@ class DboSource extends DataSource { * Format indexes for create table * * @param array $indexes - * @return string + * @param string $table + * @return array */ - function buildIndex($indexes) { + function buildIndex($indexes, $table = null) { $join = array(); foreach ($indexes as $name => $value) { $out = ''; @@ -2156,7 +2172,8 @@ class DboSource extends DataSource { } $join[] = $out; } - return join(",\n\t", $join); + return $join; } } + ?> \ No newline at end of file diff --git a/cake/tests/cases/libs/model/datasources/dbo/dbo_mysql.test.php b/cake/tests/cases/libs/model/datasources/dbo/dbo_mysql.test.php index f515feacd..af8182be5 100644 --- a/cake/tests/cases/libs/model/datasources/dbo/dbo_mysql.test.php +++ b/cake/tests/cases/libs/model/datasources/dbo/dbo_mysql.test.php @@ -194,7 +194,9 @@ class DboMysqlTest extends CakeTestCase { $this->db->cacheSources = $this->db->testing = false; $this->db->query('CREATE TABLE ' . $this->db->fullTableName('tinyint') . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id));'); - $this->model = new Model(array('name' => 'Tinyint', 'table' => $this->db->fullTableName('tinyint', false))); + $this->model = new CakeTestModel(array( + 'name' => 'Tinyint', 'table' => $this->db->fullTableName('tinyint', false) + )); $result = $this->model->schema(); $this->assertEqual($result['bool']['type'], 'boolean'); $this->assertEqual($result['small_int']['type'], 'integer'); diff --git a/cake/tests/cases/libs/model/model.test.php b/cake/tests/cases/libs/model/model.test.php index a094f3c9b..27f4c0327 100644 --- a/cake/tests/cases/libs/model/model.test.php +++ b/cake/tests/cases/libs/model/model.test.php @@ -2484,12 +2484,13 @@ class ModelTest extends CakeTestCase { $this->assertIdentical($model->Comment->find('count'), 0); - $result = $model->saveAll(array( - 'Article' => array('title' => 'Post with Author', 'body' => 'This post will be saved without an author'), - 'Comment' => array( - array('comment' => 'Only new comment'), - ) - ), array('validate' => 'first')); + $result = $model->saveAll( + array( + 'Article' => array('title' => 'Post with Author', 'body' => 'This post will be saved with an author', 'user_id' => 2), + 'Comment' => array(array('comment' => 'Only new comment', 'user_id' => 2)) + ), + array('validate' => 'first') + ); $this->assertIdentical($result, true); $result = $model->Comment->find('all');