Skip to content

Commit

Permalink
Merge pull request #1291 from markstory/3.0-postgres-create
Browse files Browse the repository at this point in the history
3.0 - Postgres schema create + refactoring
  • Loading branch information
markstory committed May 23, 2013
2 parents d73ea88 + b6d1d1c commit 5290597
Show file tree
Hide file tree
Showing 9 changed files with 783 additions and 112 deletions.
5 changes: 4 additions & 1 deletion lib/Cake/Database/Schema/Collection.php
Expand Up @@ -89,7 +89,10 @@ public function describe($name) {
}

$table = new Table($name);
$fieldParams = $this->_dialect->extraSchemaColumns();
$fieldParams = [];
if (method_exists($this->_dialect, 'extraSchemaColumn')) {
$fieldParams = $this->_dialect->extraSchemaColumns();
}
foreach ($statement->fetchAll('assoc') as $row) {
$this->_dialect->convertFieldDescription($table, $row, $fieldParams);
}
Expand Down
53 changes: 40 additions & 13 deletions lib/Cake/Database/Schema/MysqlSchema.php
Expand Up @@ -20,7 +20,7 @@
use Cake\Error;

/**
* Schema dialect/support for MySQL
* Schema management/reflection features for MySQL
*/
class MysqlSchema {

Expand All @@ -35,6 +35,7 @@ class MysqlSchema {
* Constructor
*
* @param Cake\Database\Driver $driver The driver to use.
* @return void
*/
public function __construct($driver) {
$this->_driver = $driver;
Expand Down Expand Up @@ -170,13 +171,15 @@ public function extraSchemaColumns() {
/**
* Generate the SQL to create a table.
*
* @param string $table The name of the table.
* @param array $lines The lines (columns + indexes) to go inside the table.
* @return string A complete CREATE TABLE statement
* @param Cake\Database\Schema\Table $table Table instance
* @param array $columns The columns to go inside the table.
* @param array $constraints The constraints for the table.
* @param array $indexes The indexes for the table.
* @return array Complete CREATE TABLE statement(s)
*/
public function createTableSql($table, $lines) {
$content = implode(",\n", $lines);
return sprintf("CREATE TABLE `%s` (\n%s\n);", $table, $content);
public function createTableSql($table, $columns, $constraints, $indexes) {
$content = implode(",\n", array_merge($columns, $constraints, $indexes));
return [sprintf("CREATE TABLE `%s` (\n%s\n)", $table->name(), $content)];
}

/**
Expand Down Expand Up @@ -255,33 +258,57 @@ public function columnSql(Table $table, $name) {
return $out;
}


/**
* Generate the SQL fragment for a single index in MySQL
* Generate the SQL fragments for defining table constraints.
*
* @param Cake\Database\Schema\Table $table The table object the column is in.
* @param string $name The name of the column.
* @return string SQL fragment.
*/
public function indexSql(Table $table, $name) {
$data = $table->index($name);
if ($data['type'] === Table::INDEX_PRIMARY) {
public function constraintSql(Table $table, $name) {
$data = $table->constraint($name);
if ($data['type'] === Table::CONSTRAINT_PRIMARY) {
$columns = array_map(
[$this->_driver, 'quoteIdentifier'],
$data['columns']
);
return sprintf('PRIMARY KEY (%s)', implode(', ', $columns));
}
if ($data['type'] === Table::INDEX_UNIQUE) {
if ($data['type'] === Table::CONSTRAINT_UNIQUE) {
$out = 'UNIQUE KEY ';
}
$out .= $this->_driver->quoteIdentifier($name);
return $this->_keySql($out, $data);
}

/**
* Generate the SQL fragment for a single index in MySQL
*
* @param Cake\Database\Schema\Table $table The table object the column is in.
* @param string $name The name of the column.
* @return string SQL fragment.
*/
public function indexSql(Table $table, $name) {
$data = $table->index($name);
if ($data['type'] === Table::INDEX_INDEX) {
$out = 'KEY ';
}
if ($data['type'] === Table::INDEX_FULLTEXT) {
$out = 'FULLTEXT KEY ';
}
$out .= $this->_driver->quoteIdentifier($name);
return $this->_keySql($out, $data);
}

/**
* Helper method for generating key SQL snippets.
*
* @param string $prefix The key prefix
* @param array $data Key data.
* @return string
*/
protected function _keySql($prefix, $data) {
$columns = array_map(
[$this->_driver, 'quoteIdentifier'],
$data['columns']
Expand All @@ -291,7 +318,7 @@ public function indexSql(Table $table, $name) {
$columns[$i] .= sprintf('(%d)', $data['length'][$column]);
}
}
return $out . ' (' . implode(', ', $columns) . ')';
return $prefix . ' (' . implode(', ', $columns) . ')';
}

}
174 changes: 159 additions & 15 deletions lib/Cake/Database/Schema/PostgresSchema.php
Expand Up @@ -19,6 +19,9 @@
use Cake\Database\Schema\Table;
use Cake\Error;

/**
* Schema management/reflection features for Postgres.
*/
class PostgresSchema {

/**
Expand All @@ -28,6 +31,12 @@ class PostgresSchema {
*/
protected $_driver;

/**
* Constructor
*
* @param Cake\Database\Driver\Postgres $driver Driver to use.
* @return void
*/
public function __construct($driver) {
$this->_driver = $driver;
}
Expand Down Expand Up @@ -136,19 +145,6 @@ public function convertColumn($column) {
return ['type' => 'text', 'length' => null];
}

/**
* Get additional column meta data used in schema reflections.
*
* @return array
*/
public function extraSchemaColumns() {
return [
'comment' => [
'column' => 'comment',
]
];
}

/**
* Convert field description results into abstract schema fields.
*
Expand Down Expand Up @@ -181,11 +177,159 @@ public function convertFieldDescription(Table $table, $row, $fieldParams = []) {
}
$table->addColumn($row['name'], $field);
if (!empty($row['pk'])) {
$table->addIndex('primary', [
'type' => Table::INDEX_PRIMARY,
$table->addConstraint('primary', [
'type' => Table::CONSTRAINT_PRIMARY,
'columns' => [$row['name']]
]);
}
}

/**
* Generate the SQL fragment for a single column.
*
* @param Cake\Database\Schema\Table $table The table object the column is in.
* @param string $name The name of the column.
* @return string SQL fragment.
*/
public function columnSql(Table $table, $name) {
$data = $table->column($name);
$out = $this->_driver->quoteIdentifier($name);
$typeMap = [
'biginteger' => ' BIGINT',
'boolean' => ' BOOLEAN',
'binary' => ' BYTEA',
'float' => ' FLOAT',
'decimal' => ' DECIMAL',
'text' => ' TEXT',
'date' => ' DATE',
'time' => ' TIME',
'datetime' => ' TIMESTAMP',
'timestamp' => ' TIMESTAMP',
];

if (isset($typeMap[$data['type']])) {
$out .= $typeMap[$data['type']];
}

if ($data['type'] === 'integer') {
$type = ' INTEGER';
if (in_array($name, (array)$table->primaryKey())) {
$type = ' SERIAL';
unset($data['null'], $data['default']);
}
$out .= $type;
}

if ($data['type'] === 'string') {
$isFixed = !empty($data['fixed']);
$type = ' VARCHAR';
if ($isFixed) {
$type = ' CHAR';
}
if ($isFixed && isset($data['length']) && $data['length'] == 36) {
$type = ' UUID';
}
$out .= $type;
if (isset($data['length']) && $data['length'] != 36) {
$out .= '(' . (int)$data['length'] . ')';
}
}

if ($data['type'] === 'float' && isset($data['precision'])) {
$out .= '(' . (int)$data['precision'] . ')';
}

if ($data['type'] === 'decimal' &&
(isset($data['length']) || isset($data['precision']))
) {
$out .= '(' . (int)$data['length'] . ',' . (int)$data['precision'] . ')';
}

if (isset($data['null']) && $data['null'] === false) {
$out .= ' NOT NULL';
}
if (isset($data['null']) && $data['null'] === true) {
$out .= ' DEFAULT NULL';
unset($data['default']);
}
if (isset($data['default']) && $data['type'] !== 'timestamp') {
$out .= ' DEFAULT ' . $this->_driver->schemaValue($data['default']);
}
return $out;
}

/**
* Generate the SQL fragment for a single index
*
* @param Cake\Database\Schema\Table $table The table object the column is in.
* @param string $name The name of the column.
* @return string SQL fragment.
*/
public function indexSql(Table $table, $name) {
$data = $table->index($name);
$columns = array_map(
[$this->_driver, 'quoteIdentifier'],
$data['columns']
);
return sprintf('CREATE INDEX %s ON %s (%s)',
$this->_driver->quoteIdentifier($name),
$this->_driver->quoteIdentifier($table->name()),
implode(', ', $columns)
);
}

/**
* Generate the SQL fragment for a single constraint
*
* @param Cake\Database\Schema\Table $table The table object the column is in.
* @param string $name The name of the column.
* @return string SQL fragment.
*/
public function constraintSql(Table $table, $name) {
$data = $table->constraint($name);
$out = 'CONSTRAINT ' . $this->_driver->quoteIdentifier($name);
if ($data['type'] === Table::CONSTRAINT_PRIMARY) {
$out = 'PRIMARY KEY ';
}
if ($data['type'] === Table::CONSTRAINT_UNIQUE) {
$out .= ' UNIQUE ';
}
$columns = array_map(
[$this->_driver, 'quoteIdentifier'],
$data['columns']
);
return $out . '(' . implode(', ', $columns) . ')';
}

/**
* Generate the SQL to create a table.
*
* @param Cake\Database\Schema\Table $table Table instance.
* @param array $columns The columns to go inside the table.
* @param array $constraints The constraints for the table.
* @param array $indexes The indexes for the table.
* @return string Complete CREATE TABLE statement
*/
public function createTableSql(Table $table, $columns, $constraints, $indexes) {
$content = array_merge($columns, $constraints);
$content = implode(",\n", array_filter($content));
$tableName = $this->_driver->quoteIdentifier($table->name());
$out = [];
$out[] = sprintf("CREATE TABLE %s (\n%s\n)", $tableName, $content);
foreach ($indexes as $index) {
$out[] = $index;
}
foreach ($table->columns() as $column) {
$columnData = $table->column($column);
if (isset($columnData['comment'])) {
$out[] = sprintf('COMMENT ON COLUMN %s.%s IS %s',
$tableName,
$this->_driver->quoteIdentifier($column),
$this->_driver->schemaValue($columnData['comment'])
);
}
}
return $out;
}

}

0 comments on commit 5290597

Please sign in to comment.