Skip to content

Commit

Permalink
Update schema reflection for postgres.
Browse files Browse the repository at this point in the history
Use the schema reflection query from 3.x to provide access to the serial
column attributes. These attributes give a more reliable way to access
primary key data than comparing the primary key of the model. The old
approach failed when schema was generated and concrete model's were
missing.

Refs #10356
  • Loading branch information
markstory committed Mar 27, 2017
1 parent e285df4 commit 0b6c23d
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 20 deletions.
50 changes: 35 additions & 15 deletions lib/Cake/Model/Datasource/Database/Postgres.php
Expand Up @@ -198,14 +198,26 @@ public function describe($model) {
$fields = parent::describe($table);
$this->_sequenceMap[$table] = array();
$cols = null;
$hasPrimary = false;

if ($fields === null) {
$cols = $this->_execute(
"SELECT DISTINCT table_schema AS schema, column_name AS name, data_type AS type, is_nullable AS null,
column_default AS default, ordinal_position AS position, character_maximum_length AS char_length,
character_octet_length AS oct_length FROM information_schema.columns
WHERE table_name = ? AND table_schema = ? ORDER BY position",
array($table, $this->config['schema'])
'SELECT DISTINCT table_schema AS schema,
column_name AS name,
data_type AS type,
is_nullable AS null,
column_default AS default,
ordinal_position AS position,
character_maximum_length AS char_length,
character_octet_length AS oct_length,
pg_get_serial_sequence(attr.attrelid::regclass::text, attr.attname) IS NOT NULL AS has_serial
FROM information_schema.columns c
INNER JOIN pg_catalog.pg_namespace ns ON (ns.nspname = table_schema)
INNER JOIN pg_catalog.pg_class cl ON (cl.relnamespace = ns.oid AND cl.relname = table_name)
LEFT JOIN pg_catalog.pg_attribute attr ON (cl.oid = attr.attrelid AND column_name = attr.attname)
WHERE table_name = ? AND table_schema = ? AND table_catalog = ?
ORDER BY ordinal_position',
array($table, $this->config['schema'], $this->config['database'])
);

// @codingStandardsIgnoreStart
Expand Down Expand Up @@ -238,17 +250,25 @@ public function describe($model) {
"$1",
preg_replace('/::.*/', '', $c->default)
),
'length' => $length
'length' => $length,
);
if ($model instanceof Model) {
if ($c->name === $model->primaryKey) {
$fields[$c->name]['key'] = 'primary';
if (
$fields[$c->name]['type'] !== 'string' &&
$fields[$c->name]['type'] !== 'uuid'
) {
$fields[$c->name]['length'] = 11;
}

// Serial columns are primary integer keys
if ($c->has_serial) {
$fields[$c->name]['key'] = 'primary';
$fields[$c->name]['length'] = 11;
$hasPrimary = true;
}
if ($hasPrimary === false &&
$model instanceof Model &&
$c->name === $model->primaryKey
) {
$fields[$c->name]['key'] = 'primary';
if (
$fields[$c->name]['type'] !== 'string' &&
$fields[$c->name]['type'] !== 'uuid'
) {
$fields[$c->name]['length'] = 11;
}
}
if (
Expand Down
9 changes: 4 additions & 5 deletions lib/Cake/Test/Case/Model/CakeSchemaTest.php
Expand Up @@ -688,7 +688,6 @@ public function testSchemaReadWithCrossDatabase() {
* @return void
*/
public function testSchemaReadWithNonConventionalPrimaryKey() {
$this->skipIf($this->db instanceof Postgres, 'Cannot test on Postgres');
$db = ConnectionManager::getDataSource('test');
$fixture = new NonConventionalPrimaryKeyFixture();
$fixture->create($db);
Expand All @@ -700,14 +699,14 @@ public function testSchemaReadWithNonConventionalPrimaryKey() {
));
$fixture->drop($db);

$hasTable = isset($read['tables']['non_conventional']);
$this->assertTrue($hasTable, 'non_conventional table should appear');
$this->assertArrayHasKey('non_conventional', $read['tables']);
$versionIdHasKey = isset($read['tables']['non_conventional']['version_id']['key']);
$this->assertTrue($versionIdHasKey, 'version_id key should be set');
$versionIdKeyIsPrimary = $read['tables']['non_conventional']['version_id']['key'] === 'primary';
$this->assertTrue($versionIdKeyIsPrimary, 'version_id key should be primary');
$idHasNoKey = !isset($read['tables']['non_conventional']['id']['key']);
$this->assertTrue($idHasNoKey, 'id key should not be set');

$idHasKey = isset($read['tables']['non_conventional']['id']['key']);
$this->assertFalse($idHasKey, 'id key should not be set');
}

/**
Expand Down

0 comments on commit 0b6c23d

Please sign in to comment.