Skip to content

Commit

Permalink
Implementation of table level and field level parameters support
Browse files Browse the repository at this point in the history
  • Loading branch information
skie committed Sep 29, 2009
1 parent 86d0a04 commit 627eff5
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 7 deletions.
5 changes: 5 additions & 0 deletions cake/libs/model/cake_schema.php
Expand Up @@ -237,6 +237,7 @@ function read($options = array()) {
if (empty($tables[$Object->table])) {
$tables[$Object->table] = $this->__columns($Object);
$tables[$Object->table]['indexes'] = $db->index($Object);
$tables[$Object->table]['tableParameters'] = $db->readTableParameters($table);
unset($currentTables[$key]);
}
if (!empty($Object->hasAndBelongsToMany)) {
Expand All @@ -250,6 +251,7 @@ function read($options = array()) {
$key = array_search($table, $currentTables);
$tables[$Object->$class->table] = $this->__columns($Object->$class);
$tables[$Object->$class->table]['indexes'] = $db->index($Object->$class);
$tables[$Object->$class->table]['tableParameters'] = $db->readTableParameters($table);
unset($currentTables[$key]);
}
}
Expand Down Expand Up @@ -279,12 +281,15 @@ function read($options = array()) {
if (in_array($table, $systemTables)) {
$tables[$Object->table] = $this->__columns($Object);
$tables[$Object->table]['indexes'] = $db->index($Object);
$tables[$Object->table]['tableParameters'] = $db->readTableParameters($table);
} elseif ($models === false) {
$tables[$table] = $this->__columns($Object);
$tables[$table]['indexes'] = $db->index($Object);
$tables[$table]['tableParameters'] = $db->readTableParameters($table);
} else {
$tables['missing'][$table] = $this->__columns($Object);
$tables['missing'][$table]['indexes'] = $db->index($Object);
$tables['missing'][$table]['tableParameters'] = $db->readTableParameters($table);
}
}
}
Expand Down
83 changes: 82 additions & 1 deletion cake/libs/model/datasources/dbo/dbo_mysql.php
Expand Up @@ -75,6 +75,30 @@ class DboMysqlBase extends DboSource {
'rollback' => 'ROLLBACK'
);

/**
* List of engine specific additional field parameters used on table creating
*
* @var array
* @access public
*/
var $fieldParameters = array(
'charset' => array('value' => 'CHARACTER SET', 'quote' => false, 'join' => ' ', 'column' => false, 'position' => 'beforeDefault'),
'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => ' ', 'column' => 'Collation', 'position' => 'beforeDefault'),
'comment' => array('value' => 'COMMENT', 'quote' => true, 'join' => ' ', 'column' => 'Comment', 'position' => 'afterDefault')
);

/**
* List of table engine specific parameters used on table creating
*
* @var array
* @access public
*/
var $tableParameters = array(
'charset' => array('value' => 'DEFAULT CHARSET', 'quote' => false, 'join' => '=', 'column' => 'charset'),
'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => '=', 'column' => 'Collation'),
'engine' => array('value' => 'ENGINE', 'quote' => false, 'join' => '=', 'column' => 'Engine')
);

/**
* MySQL column definition
*
Expand Down Expand Up @@ -457,6 +481,38 @@ function listSources() {
}
}

/**
* Returns an detailed array of sources (tables) in the database.
*
* @param string $name Table name to get parameters
* @return array Array of tablenames in the database
*/
function listDetailedSources($name = null) {
$condition = '';
if (is_string($name)) {
$condition = ' WHERE Name=' . $this->value($name);
}
$result = $this->query('SHOW TABLE STATUS FROM ' . $this->name($this->config['database']) . $condition . ';');
if (!$result) {
return array();
} else {
$tables = array();
foreach ($result as $row) {
$tables[$row['TABLES']['Name']] = $row['TABLES'];
if (!empty($row['TABLES']['Collation'])) {
$charset = $this->getCharsetName($row['TABLES']['Collation']);
if ($charset) {
$tables[$row['TABLES']['Name']]['charset'] = $charset;
}
}
}
if (is_string($name)) {
return $tables[$name];
}
return $tables;
}
}

/**
* Returns an array of the fields in given table name.
*
Expand All @@ -469,7 +525,7 @@ function describe(&$model) {
return $cache;
}
$fields = false;
$cols = $this->query('DESCRIBE ' . $this->fullTableName($model));
$cols = $this->query('SHOW FULL COLUMNS FROM ' . $this->fullTableName($model));

foreach ($cols as $column) {
$colKey = array_keys($column);
Expand All @@ -486,12 +542,37 @@ function describe(&$model) {
if (!empty($column[0]['Key']) && isset($this->index[$column[0]['Key']])) {
$fields[$column[0]['Field']]['key'] = $this->index[$column[0]['Key']];
}
foreach ($this->fieldParameters as $name => $value) {
if (!empty($column[0][$value['column']])) {
$fields[$column[0]['Field']][$name] = $column[0][$value['column']];
}
}
if (isset($fields[$column[0]['Field']]['collate'])) {
$charset = $this->getCharsetName($fields[$column[0]['Field']]['collate']);
if ($charset) {
$fields[$column[0]['Field']]['charset'] = $charset;
}
}
}
}
$this->__cacheDescription($this->fullTableName($model, false), $fields);
return $fields;
}

/**
* Query charset by collation
*
* @param string $name Collation name
* @return string Character set name
*/
function getCharsetName($name) {
$cols = $this->query('SELECT CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE COLLATION_NAME= ' . $this->value($name) . ';');
if (isset($cols[0]['COLLATIONS']['CHARACTER_SET_NAME'])) {
return $cols[0]['COLLATIONS']['CHARACTER_SET_NAME'];
}
return false;
}

/**
* Returns a quoted and escaped string of $data for use in an SQL statement.
*
Expand Down
93 changes: 87 additions & 6 deletions cake/libs/model/datasources/dbo_source.php
Expand Up @@ -91,6 +91,22 @@ class DboSource extends DataSource {
'rollback' => 'ROLLBACK'
);

/**
* List of table engine specific parameters used on table creating
*
* @var array
* @access protected
*/
var $tableParameters = array();

/**
* List of engine specific additional field parameters used on table creating
*
* @var array
* @access protected
*/
var $fieldParameters = array();

/**
* Constructor
*/
Expand Down Expand Up @@ -1370,15 +1386,17 @@ function renderStatement($type, $data) {
return "DELETE {$alias} FROM {$table} {$aliases}{$conditions}";
break;
case 'schema':
foreach (array('columns', 'indexes') as $var) {
foreach (array('columns', 'indexes', 'tableParameters') as $var) {
if (is_array(${$var})) {
${$var} = "\t" . join(",\n\t", array_filter(${$var}));
} else {
${$var} = '';
}
}
if (trim($indexes) != '') {
$columns .= ',';
}
return "CREATE TABLE {$table} (\n{$columns}{$indexes});";
return "CREATE TABLE {$table} (\n{$columns}{$indexes}){$tableParameters};";
break;
case 'alter':
break;
Expand Down Expand Up @@ -2334,7 +2352,7 @@ function createSchema($schema, $tableName = null) {

foreach ($schema->tables as $curTable => $columns) {
if (!$tableName || $tableName == $curTable) {
$cols = $colList = $indexes = array();
$cols = $colList = $indexes = $tableParameters = array();
$primary = null;
$table = $this->fullTableName($curTable);

Expand All @@ -2345,22 +2363,24 @@ function createSchema($schema, $tableName = null) {
if (isset($col['key']) && $col['key'] == 'primary') {
$primary = $name;
}
if ($name !== 'indexes') {
if ($name !== 'indexes' && $name !== 'tableParameters') {
$col['name'] = $name;
if (!isset($col['type'])) {
$col['type'] = 'string';
}
$cols[] = $this->buildColumn($col);
} else {
} elseif ($name == 'indexes') {
$indexes = array_merge($indexes, $this->buildIndex($col, $table));
} elseif ($name == 'tableParameters') {
$tableParameters = array_merge($tableParameters, $this->buildTableParameters($col, $table));
}
}
if (empty($indexes) && !empty($primary)) {
$col = array('PRIMARY' => array('column' => $primary, 'unique' => 1));
$indexes = array_merge($indexes, $this->buildIndex($col, $table));
}
$columns = $cols;
$out .= $this->renderStatement('schema', compact('table', 'columns', 'indexes')) . "\n\n";
$out .= $this->renderStatement('schema', compact('table', 'columns', 'indexes', 'tableParameters')) . "\n\n";
}
}
return $out;
Expand Down Expand Up @@ -2440,6 +2460,16 @@ function buildColumn($column) {
$column['default'] = null;
}

foreach ($this->fieldParameters as $paramName => $value) {
if (isset($column[$paramName]) && $value['position'] == 'beforeDefault') {
$val = $column[$paramName];
if ($value['quote']) {
$val = $this->value($val);
}
$out .= ' ' . $value['value'] . $value['join'] . $val;
}
}

if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') {
$out .= ' ' . $this->columns['primary_key']['name'];
} elseif (isset($column['key']) && $column['key'] == 'primary') {
Expand All @@ -2453,6 +2483,17 @@ function buildColumn($column) {
} elseif (isset($column['null']) && $column['null'] == false) {
$out .= ' NOT NULL';
}

foreach ($this->fieldParameters as $paramName => $value) {
if (isset($column[$paramName]) && $value['position'] == 'afterDefault') {
$val = $column[$paramName];
if ($value['quote']) {
$val = $this->value($val);
}
$out .= ' ' . $value['value'] . $value['join'] . $val;
}
}

return $out;
}

Expand Down Expand Up @@ -2486,6 +2527,46 @@ function buildIndex($indexes, $table = null) {
return $join;
}

/**
* Read additional table parameters
*
* @param array $parameters
* @param string $table
* @return array
*/
function readTableParameters($name) {
$parameters = array();
if ($this->isInterfaceSupported('listDetailedSources')) {
$currentTableDetails = $this->listDetailedSources($name);
foreach ($this->tableParameters as $paramName => $parameter) {
if (!empty($parameter['column']) && !empty($currentTableDetails[$parameter['column']])) {
$parameters[$paramName] = $currentTableDetails[$parameter['column']];
}
}
}
return $parameters;
}

/**
* Format parameters for create table
*
* @param array $parameters
* @param string $table
* @return array
*/
function buildTableParameters($parameters, $table = null) {
$result = array();
foreach ($parameters as $name => $value) {
if (isset($this->tableParameters[$name])) {
if ($this->tableParameters[$name]['quote']) {
$value = $this->value($value);
}
$result[] = $this->tableParameters[$name]['value'] . $this->tableParameters[$name]['join'] . $value;
}
}
return $result;
}

/**
* Guesses the data type of an array
*
Expand Down
34 changes: 34 additions & 0 deletions cake/tests/cases/libs/model/datasources/dbo/dbo_mysql.test.php
Expand Up @@ -385,6 +385,40 @@ function testIndexDetection() {
$this->db->query('DROP TABLE ' . $name);
}

/**
* testBuildColumn method
*
* @access public
* @return void
*/
function testBuildColumn() {
$this->db->columns = array('varchar(255)' => 1);
$data = array(
'name' => 'testName',
'type' => 'varchar(255)',
'default',
'null' => true,
'key',
'comment' => 'test'
);
$result = $this->db->buildColumn($data);
$expected = '`testName` DEFAULT NULL COMMENT \'test\'';
$this->assertEqual($result, $expected);

$data = array(
'name' => 'testName',
'type' => 'varchar(255)',
'default',
'null' => true,
'key',
'charset' => 'utf8',
'collate' => 'utf8_unicode_ci'
);
$result = $this->db->buildColumn($data);
$expected = '`testName` CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL';
$this->assertEqual($result, $expected);
}

/**
* MySQL 4.x returns index data in a different format,
* Using a mock ensure that MySQL 4.x output is properly parsed.
Expand Down

0 comments on commit 627eff5

Please sign in to comment.