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
This commit is contained in:
nate 2008-05-18 06:42:36 +00:00
parent e1fc6ad5fd
commit 6bed503b56
5 changed files with 166 additions and 35 deletions

View file

@ -597,28 +597,62 @@ class DboPostgres extends DboSource {
* Format indexes for create table * Format indexes for create table
* *
* @param array $indexes * @param array $indexes
* @param string $table
* @return string * @return string
*/ */
function buildIndex($indexes) { function buildIndex($indexes, $table = null) {
$join = array(); $join = array();
foreach ($indexes as $name => $value) { foreach ($indexes as $name => $value) {
$out = '';
if ($name == 'PRIMARY') { if ($name == 'PRIMARY') {
$out .= 'PRIMARY '; $out = 'PRIMARY KEY (' . $this->name($value['column']) . ')';
$name = null;
} else { } else {
$out = 'CREATE ';
if (!empty($value['unique'])) { if (!empty($value['unique'])) {
$name .= ' UNIQUE '; $out .= 'UNIQUE ';
}
} }
if (is_array($value['column'])) { if (is_array($value['column'])) {
$out .= 'CONSTRAINT '. $name .' (' . join(', ', array_map(array(&$this, 'name'), $value['column'])) . ')'; $value['column'] = join(', ', array_map(array(&$this, 'name'), $value['column']));
} else { } else {
$out .= 'KEY '. $name .' (' . $this->name($value['column']) . ')'; $value['column'] = $this->name($value['column']);
}
$out .= "INDEX {$name} ON {$table}({$value['column']});";
} }
$join[] = $out; $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;
}
} }
} }

View file

@ -93,6 +93,10 @@ class DboSqlite extends DboSource {
$config = $this->config; $config = $this->config;
$this->connection = $config['connect']($config['database']); $this->connection = $config['connect']($config['database']);
$this->connected = is_resource($this->connection); $this->connected = is_resource($this->connection);
if ($this->connected) {
$this->_execute('PRAGMA count_changes = 1');
}
return $this->connected; return $this->connected;
} }
/** /**
@ -186,7 +190,7 @@ class DboSqlite extends DboSource {
* @param string $data String to be prepared for use in an SQL statement * @param string $data String to be prepared for use in an SQL statement
* @return string Quoted and escaped * @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); $parent = parent::value($data, $column, $safe);
if ($parent != null) { if ($parent != null) {
@ -237,7 +241,7 @@ class DboSqlite extends DboSource {
* @return boolean True on success, false on fail * @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions). * (i.e. if the database/model does not support transactions).
*/ */
function begin (&$model) { function begin(&$model) {
if (parent::begin($model)) { if (parent::begin($model)) {
if ($this->execute('BEGIN')) { if ($this->execute('BEGIN')) {
$this->_transactionStarted = true; $this->_transactionStarted = true;
@ -254,7 +258,7 @@ class DboSqlite extends DboSource {
* (i.e. if the database/model does not support transactions, * (i.e. if the database/model does not support transactions,
* or a transaction has not started). * or a transaction has not started).
*/ */
function commit (&$model) { function commit(&$model) {
if (parent::commit($model)) { if (parent::commit($model)) {
$this->_transactionStarted = false; $this->_transactionStarted = false;
return $this->execute('COMMIT'); return $this->execute('COMMIT');
@ -269,7 +273,7 @@ class DboSqlite extends DboSource {
* (i.e. if the database/model does not support transactions, * (i.e. if the database/model does not support transactions,
* or a transaction has not started). * or a transaction has not started).
*/ */
function rollback (&$model) { function rollback(&$model) {
if (parent::rollback($model)) { if (parent::rollback($model)) {
return $this->execute('ROLLBACK'); return $this->execute('ROLLBACK');
} }
@ -485,6 +489,79 @@ class DboSqlite extends DboSource {
} }
return $out; 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;
}
}
} }
?> ?>

View file

@ -1232,6 +1232,19 @@ class DboSource extends DataSource {
} }
return "DELETE {$alias} FROM {$table} {$aliases}{$conditions}"; return "DELETE {$alias} FROM {$table} {$aliases}{$conditions}";
break; 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 * Generate a database-native schema for the given Schema object
* *
* @param object $schema An instance of a subclass of CakeSchema * @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. * Otherwise, all tables defined in the schema are generated.
* @return string * @return string
*/ */
function createSchema($schema, $table = null) { function createSchema($schema, $tableName = null) {
if (!is_a($schema, 'CakeSchema')) { if (!is_a($schema, 'CakeSchema')) {
trigger_error(__('Invalid schema object', true), E_USER_WARNING); trigger_error(__('Invalid schema object', true), E_USER_WARNING);
return null; return null;
@ -2019,10 +2032,11 @@ class DboSource extends DataSource {
$out = ''; $out = '';
foreach ($schema->tables as $curTable => $columns) { foreach ($schema->tables as $curTable => $columns) {
if (!$table || $table == $curTable) { if (!$tableName || $tableName == $curTable) {
$out .= 'CREATE TABLE ' . $this->fullTableName($curTable) . " (\n"; $cols = $colList = $indexes = array();
$cols = $colList = $index = array();
$primary = null; $primary = null;
$table = $this->fullTableName($curTable);
foreach ($columns as $name => $col) { foreach ($columns as $name => $col) {
if (is_string($col)) { if (is_string($col)) {
$col = array('type' => $col); $col = array('type' => $col);
@ -2037,14 +2051,15 @@ class DboSource extends DataSource {
} }
$cols[] = $this->buildColumn($col); $cols[] = $this->buildColumn($col);
} else { } else {
$index[] = $this->buildIndex($col); $indexes = array_merge($indexes, $this->buildIndex($col, $table));
} }
} }
if (empty($index) && !empty($primary)) { if (empty($indexes) && !empty($primary)) {
$col = array('PRIMARY' => array('column'=> $primary, 'unique' => 1)); $col = array('PRIMARY' => array('column' => $primary, 'unique' => 1));
$index[] = $this->buildIndex($col); $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; return $out;
@ -2135,9 +2150,10 @@ class DboSource extends DataSource {
* Format indexes for create table * Format indexes for create table
* *
* @param array $indexes * @param array $indexes
* @return string * @param string $table
* @return array
*/ */
function buildIndex($indexes) { function buildIndex($indexes, $table = null) {
$join = array(); $join = array();
foreach ($indexes as $name => $value) { foreach ($indexes as $name => $value) {
$out = ''; $out = '';
@ -2156,7 +2172,8 @@ class DboSource extends DataSource {
} }
$join[] = $out; $join[] = $out;
} }
return join(",\n\t", $join); return $join;
} }
} }
?> ?>

View file

@ -194,7 +194,9 @@ class DboMysqlTest extends CakeTestCase {
$this->db->cacheSources = $this->db->testing = false; $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->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(); $result = $this->model->schema();
$this->assertEqual($result['bool']['type'], 'boolean'); $this->assertEqual($result['bool']['type'], 'boolean');
$this->assertEqual($result['small_int']['type'], 'integer'); $this->assertEqual($result['small_int']['type'], 'integer');

View file

@ -2484,12 +2484,13 @@ class ModelTest extends CakeTestCase {
$this->assertIdentical($model->Comment->find('count'), 0); $this->assertIdentical($model->Comment->find('count'), 0);
$result = $model->saveAll(array( $result = $model->saveAll(
'Article' => array('title' => 'Post with Author', 'body' => 'This post will be saved without an author'), array(
'Comment' => array( 'Article' => array('title' => 'Post with Author', 'body' => 'This post will be saved with an author', 'user_id' => 2),
array('comment' => 'Only new comment'), 'Comment' => array(array('comment' => 'Only new comment', 'user_id' => 2))
) ),
), array('validate' => 'first')); array('validate' => 'first')
);
$this->assertIdentical($result, true); $this->assertIdentical($result, true);
$result = $model->Comment->find('all'); $result = $model->Comment->find('all');