Skip to content

Commit

Permalink
Added support to drop index and reset sequence;
Browse files Browse the repository at this point in the history
  • Loading branch information
edgardmessias committed Jul 15, 2015
1 parent e3666ea commit 9d39ca2
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 79 deletions.
80 changes: 51 additions & 29 deletions QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_MONEY => 'numeric(18,4)',
];

/**
* @inheritdoc
*/
public function buildSelect($columns, &$params, $distinct = false, $selectOption = null)
{
if (is_array($columns)) {
Expand All @@ -59,6 +62,9 @@ public function buildSelect($columns, &$params, $distinct = false, $selectOption
return parent::buildSelect($columns, $params, $distinct, $selectOption);
}

/**
* @inheritdoc
*/
protected function buildCompositeInCondition($operator, $columns, $values, &$params)
{
$quotedColumns = [];
Expand All @@ -82,6 +88,9 @@ protected function buildCompositeInCondition($operator, $columns, $values, &$par
return '(' . implode($operator === 'IN' ? ' OR ' : ' AND ', $vss) . ')';
}

/**
* @inheritdoc
*/
public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)
{

Expand Down Expand Up @@ -125,15 +134,14 @@ public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)
return $sql;
}

/**
* @inheritdoc
*/
public function insert($table, $columns, &$params)
{
$schema = $this->db->getSchema();
$autoIncrementColumn = null;
if (($tableSchema = $schema->getTableSchema($table)) !== null) {
$columnSchemas = $tableSchema->columns;
if ($tableSchema->sequenceName !== null) {
$autoIncrementColumn = $tableSchema->sequenceName;
}
} else {
$columnSchemas = [];
}
Expand All @@ -143,15 +151,13 @@ public function insert($table, $columns, &$params)
$columns[$name] = [$value, 'blob'];
}
}
$sql = parent::insert($table, $columns, $params);

if ($autoIncrementColumn !== null) {
$sql .= ' RETURNING ' . $autoIncrementColumn;
}

return $sql;
return parent::insert($table, $columns, $params);
}

/**
* @inheritdoc
*/
public function update($table, $columns, $condition, &$params)
{
$schema = $this->db->getSchema();
Expand All @@ -169,25 +175,7 @@ public function update($table, $columns, $condition, &$params)
}

/**
* Generates a batch INSERT SQL statement.
* For example,
*
* ~~~
* $sql = $queryBuilder->batchInsert('user', ['name', 'age'], [
* ['Tom', 30],
* ['Jane', 20],
* ['Linda', 25],
* ]);
* ~~~
*
* Note that the values in each row must match the corresponding column names.
*
* The method will properly escape the column names, and quote the values to be inserted.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
* @param array $rows the rows to be batch inserted into the table
* @return string the batch INSERT SQL statement
* @inheritdoc
*/
public function batchInsert($table, $columns, $rows)
{
Expand Down Expand Up @@ -308,4 +296,38 @@ public function alterColumn($table, $column, $type)
return $sql;
}
}

/**
* @inheritdoc
*/
public function dropIndex($name, $table)
{
return 'DROP INDEX ' . $this->db->quoteTableName($name);
}

/**
* @inheritdoc
*/
public function resetSequence($table, $value = null)
{
$tableSchema = $this->db->getTableSchema($table);
if ($tableSchema === null) {
throw new InvalidParamException("Table not found: $table");
}
if ($tableSchema->sequenceName === null) {
throw new InvalidParamException("There is not sequence associated with table '$table'.");
}

if ($value !== null) {
$value = (int) $value;
} else {
// use master connection to get the biggest PK value
$value = $this->db->useMaster(function (Connection $db) use ($tableSchema) {
$key = reset($tableSchema->primaryKey);
return $db->createCommand("SELECT MAX({$this->db->quoteColumnName($key)}) FROM {$this->db->quoteTableName($tableSchema->name)}")->queryScalar();
}) + 1;
}

return "ALTER SEQUENCE {$this->db->quoteColumnName($tableSchema->sequenceName)} RESTART WITH $value";
}
}
86 changes: 40 additions & 46 deletions Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
class Schema extends \yii\db\Schema
{

private $_sequences = [];
private $_lastInsertID = null;

/**
Expand Down Expand Up @@ -95,16 +94,6 @@ protected function loadTableSchema($name)
$this->resolveTableNames($table, $name);
if ($this->findColumns($table)) {
$this->findConstraints($table);
if (is_string($table->primaryKey) && isset($this->_sequences[$table->fullName . '.' . $table->primaryKey])) {
$table->sequenceName = $this->_sequences[$table->fullName . '.' . $table->primaryKey];
} elseif (is_array($table->primaryKey)) {
foreach ($table->primaryKey as $pk) {
if (isset($this->_sequences[$table->fullName . '.' . $pk])) {
$table->sequenceName = $this->_sequences[$table->fullName . '.' . $pk];
break;
}
}
}
return $table;
}
return null;
Expand Down Expand Up @@ -164,7 +153,7 @@ protected function findColumns($table)
fld.rdb$field_precision AS fprecision,
rel.rdb$null_flag AS fnull,
fld.rdb$default_value AS fdefault_value,
(SELECT 1 FROM RDB$TRIGGERS
(SELECT RDB$TRIGGER_SOURCE FROM RDB$TRIGGERS
WHERE RDB$SYSTEM_FLAG = 0
AND UPPER(RDB$RELATION_NAME)=UPPER(\'' . $table->name . '\')
AND RDB$TRIGGER_TYPE = 1
Expand Down Expand Up @@ -207,8 +196,13 @@ protected function findColumns($table)
}
foreach ($columns as $column) {
$c = $this->loadColumnSchema($column);
if ($c->autoIncrement) {
$this->_sequences[$table->fullName . '.' . $c->name] = $table->fullName . '.' . $c->name;
if ($table->sequenceName === null && $c->autoIncrement) {
$matches = [];
if (preg_match("/NEW.{$c->name}\s*=\s*GEN_ID\((\w+)/i", $column['fautoinc'], $matches)) {
$table->sequenceName = $matches[1];
} elseif (preg_match("/NEW.{$c->name}\s*=\s*NEXT\s+VALUE\s+FOR\s+(\w+)/i", $column['fautoinc'], $matches)) {
$table->sequenceName = $matches[1];
}
}
$table->columns[$c->name] = $c;
if ($c->isPrimaryKey) {
Expand Down Expand Up @@ -239,7 +233,7 @@ protected function loadColumnSchema($column)
$c->name = strtolower(rtrim($column['fname']));
$c->allowNull = $column['fnull'] !== '1';
$c->isPrimaryKey = $column['fprimary'];
$c->autoIncrement = $column['fautoinc'] === '1';
$c->autoIncrement = (boolean)$column['fautoinc'];

$c->type = self::TYPE_STRING;

Expand Down Expand Up @@ -345,7 +339,7 @@ protected function loadColumnSchema($column)

$c->defaultValue = null;
if ($defaultValue !== null) {
if (in_array($c->type, [self::TYPE_DATE, self::TYPE_DATETIME, self::TYPE_TIME, self::TYPE_TIMESTAMP])
if (in_array($c->type, [self::TYPE_DATE, self::TYPE_DATETIME, self::TYPE_TIME, self::TYPE_TIMESTAMP])
&& preg_match('/(CURRENT_|NOW|NULL|TODAY|TOMORROW|YESTERDAY)/i', $defaultValue)) {
$c->defaultValue = new \yii\db\Expression(trim($defaultValue));
} else {
Expand Down Expand Up @@ -443,53 +437,53 @@ public function setTransactionIsolationLevel($level)
}

/**
* Executes the INSERT command, returning primary key values.
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column data (name => value) to be inserted into the table.
* @return array primary key values or false if the command fails
* @since 2.0.4
* @inheritdoc
*/
public function insert($table, $columns)
{
$this->_lastInsertID = false;
$params = [];
$sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);
$returnColumns = $this->getTableSchema($table)->primaryKey;
if (!empty($returnColumns)) {
$returning = [];
foreach ((array)$returnColumns as $name) {
$returning[] = $this->quoteColumnName($name);
}
$sql .= ' RETURNING ' . implode(', ', $returning);
}

$tableSchema = $this->getTableSchema($table);

$command = $this->db->createCommand()->insert($table, $columns);
$command = $this->db->createCommand($sql, $params);
$command->prepare(false);
$result = $command->queryOne();

if ($tableSchema->sequenceName !== null) {
$this->_lastInsertID = $command->queryScalar();
if ($this->_lastInsertID === false) {
return false;
}
if (!$command->pdoStatement->rowCount()) {
return false;
} else {
if (!$command->execute()) {
return false;
}
}
$result = [];
foreach ($tableSchema->primaryKey as $name) {
if ($tableSchema->columns[$name]->autoIncrement) {
$result[$name] = $this->getLastInsertID($tableSchema->sequenceName);
break;
} else {
$result[$name] = isset($columns[$name]) ? $columns[$name] : $tableSchema->columns[$name]->defaultValue;
if (!empty($returnColumns)) {
foreach ((array)$returnColumns as $name) {
if ($this->getTableSchema($table)->getColumn($name)->autoIncrement) {
$this->_lastInsertID = $result[$name];
break;
}
}
}
return $result;
}
return $result;
}

/**
* Returns the ID of the last inserted row or sequence value.
* @param string $sequenceName name of the sequence object (required by some DBMS)
* @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
* @throws InvalidCallException if the DB connection is not active
* @see http://www.php.net/manual/en/function.PDO-lastInsertId.php
* @inheritdoc
*/
public function getLastInsertID($sequenceName = '')
{
if (!$this->db->isActive) {
throw new InvalidCallException('DB Connection is not active.');
}

if ($sequenceName !== '') {
return $this->db->createCommand('SELECT GEN_ID('. $this->db->quoteTableName($sequenceName) . ', 0 ) FROM RDB$DATABASE;')->queryScalar();
}

if ($this->_lastInsertID !== false) {
return $this->_lastInsertID;
Expand Down
43 changes: 43 additions & 0 deletions tests/QueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,47 @@ public function testAlterColumn()
$this->assertEquals(false, $newColumns['profile_id']->allowNull);
$this->assertEquals(0, $newColumns['profile_id']->defaultValue);
}

public function testDropIndex()
{
$connection = $this->getConnection(true);
$qb = $this->getQueryBuilder();

$this->assertEquals('DROP INDEX idx_int_col', $qb->dropIndex('idx_int_col', 'type'));

$columns = $connection->getTableSchema('type', true)->columnNames;

foreach ($columns as $column) {
$result = $connection->createCommand($qb->createIndex('idx_' .$column, 'type', $column))->execute();
}

foreach ($columns as $column) {
$result = $connection->createCommand($qb->dropIndex('idx_' .$column, 'type'))->execute();
}
}

public function testResetSequence()
{
$connection = $this->getConnection(true);
$qb = $this->getQueryBuilder();

$this->assertEquals('ALTER SEQUENCE seq_animal_id RESTART WITH 3', $qb->resetSequence('animal'));
$this->assertEquals('ALTER SEQUENCE seq_animal_id RESTART WITH 10', $qb->resetSequence('animal', 10));

$this->assertEquals('ALTER SEQUENCE gen_profile_id RESTART WITH 3', $qb->resetSequence('profile'));
$this->assertEquals('ALTER SEQUENCE gen_profile_id RESTART WITH 10', $qb->resetSequence('profile', 10));

$this->assertEquals(2, (new Query())->from('profile')->max('id', $connection));

$connection->createCommand()->insert('profile', ['description' => 'profile customer 3'])->execute();
$this->assertEquals(3, (new Query())->from('profile')->max('id', $connection));

$connection->createCommand($qb->resetSequence('profile'))->execute();
$connection->createCommand()->insert('profile', ['description' => 'profile customer 4'])->execute();
$this->assertEquals(4, (new Query())->from('profile')->max('id', $connection));

$connection->createCommand($qb->resetSequence('profile', 10))->execute();
$connection->createCommand()->insert('profile', ['description' => 'profile customer 11'])->execute();
$this->assertEquals(11, (new Query())->from('profile')->max('id', $connection));
}
}
9 changes: 9 additions & 0 deletions tests/SchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,13 @@ public function testGetPDOType()
}
fclose($fp);
}

public function testGetLastInsertID()
{
/* @var $schema Schema */
$schema = $this->getConnection()->schema;
$this->assertEquals(null, $schema->getLastInsertID());
$this->assertEquals(2, $schema->getLastInsertID($schema->getTableSchema('animal')->sequenceName));
$this->assertEquals(2, $schema->getLastInsertID($schema->getTableSchema('profile')->sequenceName));
}
}
8 changes: 4 additions & 4 deletions tests/data/source.sql
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ END;
-- SQL
EXECUTE block AS
BEGIN
IF (EXISTS(SELECT 1 FROM rdb$generators WHERE LOWER(rdb$generator_name) = 'gen_animal_id')) THEN
EXECUTE STATEMENT 'DROP GENERATOR gen_animal_id;';
IF (EXISTS(SELECT 1 FROM rdb$generators WHERE LOWER(rdb$generator_name) = 'seq_animal_id')) THEN
EXECUTE STATEMENT 'DROP GENERATOR seq_animal_id;';
END;
-- SQL
EXECUTE block AS
Expand Down Expand Up @@ -307,13 +307,13 @@ CREATE TABLE animal (
PRIMARY KEY (id)
);
-- SQL
CREATE GENERATOR gen_animal_id;
CREATE SEQUENCE seq_animal_id;
-- SQL
CREATE TRIGGER tr_animal FOR animal
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
if (NEW.ID is NULL) then NEW.ID = GEN_ID(gen_animal_id, 1);
if (NEW.ID is NULL) then NEW.ID = NEXT VALUE FOR seq_animal_id;
END
-- SQL
CREATE TABLE default_pk (
Expand Down

0 comments on commit 9d39ca2

Please sign in to comment.