Skip to content

Commit 9d39ca2

Browse files
committed
Added support to drop index and reset sequence;
1 parent e3666ea commit 9d39ca2

File tree

5 files changed

+147
-79
lines changed

5 files changed

+147
-79
lines changed

QueryBuilder.php

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ class QueryBuilder extends \yii\db\QueryBuilder
3939
Schema::TYPE_MONEY => 'numeric(18,4)',
4040
];
4141

42+
/**
43+
* @inheritdoc
44+
*/
4245
public function buildSelect($columns, &$params, $distinct = false, $selectOption = null)
4346
{
4447
if (is_array($columns)) {
@@ -59,6 +62,9 @@ public function buildSelect($columns, &$params, $distinct = false, $selectOption
5962
return parent::buildSelect($columns, $params, $distinct, $selectOption);
6063
}
6164

65+
/**
66+
* @inheritdoc
67+
*/
6268
protected function buildCompositeInCondition($operator, $columns, $values, &$params)
6369
{
6470
$quotedColumns = [];
@@ -82,6 +88,9 @@ protected function buildCompositeInCondition($operator, $columns, $values, &$par
8288
return '(' . implode($operator === 'IN' ? ' OR ' : ' AND ', $vss) . ')';
8389
}
8490

91+
/**
92+
* @inheritdoc
93+
*/
8594
public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)
8695
{
8796

@@ -125,15 +134,14 @@ public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)
125134
return $sql;
126135
}
127136

137+
/**
138+
* @inheritdoc
139+
*/
128140
public function insert($table, $columns, &$params)
129141
{
130142
$schema = $this->db->getSchema();
131-
$autoIncrementColumn = null;
132143
if (($tableSchema = $schema->getTableSchema($table)) !== null) {
133144
$columnSchemas = $tableSchema->columns;
134-
if ($tableSchema->sequenceName !== null) {
135-
$autoIncrementColumn = $tableSchema->sequenceName;
136-
}
137145
} else {
138146
$columnSchemas = [];
139147
}
@@ -143,15 +151,13 @@ public function insert($table, $columns, &$params)
143151
$columns[$name] = [$value, 'blob'];
144152
}
145153
}
146-
$sql = parent::insert($table, $columns, $params);
147-
148-
if ($autoIncrementColumn !== null) {
149-
$sql .= ' RETURNING ' . $autoIncrementColumn;
150-
}
151154

152-
return $sql;
155+
return parent::insert($table, $columns, $params);
153156
}
154157

158+
/**
159+
* @inheritdoc
160+
*/
155161
public function update($table, $columns, $condition, &$params)
156162
{
157163
$schema = $this->db->getSchema();
@@ -169,25 +175,7 @@ public function update($table, $columns, $condition, &$params)
169175
}
170176

171177
/**
172-
* Generates a batch INSERT SQL statement.
173-
* For example,
174-
*
175-
* ~~~
176-
* $sql = $queryBuilder->batchInsert('user', ['name', 'age'], [
177-
* ['Tom', 30],
178-
* ['Jane', 20],
179-
* ['Linda', 25],
180-
* ]);
181-
* ~~~
182-
*
183-
* Note that the values in each row must match the corresponding column names.
184-
*
185-
* The method will properly escape the column names, and quote the values to be inserted.
186-
*
187-
* @param string $table the table that new rows will be inserted into.
188-
* @param array $columns the column names
189-
* @param array $rows the rows to be batch inserted into the table
190-
* @return string the batch INSERT SQL statement
178+
* @inheritdoc
191179
*/
192180
public function batchInsert($table, $columns, $rows)
193181
{
@@ -308,4 +296,38 @@ public function alterColumn($table, $column, $type)
308296
return $sql;
309297
}
310298
}
299+
300+
/**
301+
* @inheritdoc
302+
*/
303+
public function dropIndex($name, $table)
304+
{
305+
return 'DROP INDEX ' . $this->db->quoteTableName($name);
306+
}
307+
308+
/**
309+
* @inheritdoc
310+
*/
311+
public function resetSequence($table, $value = null)
312+
{
313+
$tableSchema = $this->db->getTableSchema($table);
314+
if ($tableSchema === null) {
315+
throw new InvalidParamException("Table not found: $table");
316+
}
317+
if ($tableSchema->sequenceName === null) {
318+
throw new InvalidParamException("There is not sequence associated with table '$table'.");
319+
}
320+
321+
if ($value !== null) {
322+
$value = (int) $value;
323+
} else {
324+
// use master connection to get the biggest PK value
325+
$value = $this->db->useMaster(function (Connection $db) use ($tableSchema) {
326+
$key = reset($tableSchema->primaryKey);
327+
return $db->createCommand("SELECT MAX({$this->db->quoteColumnName($key)}) FROM {$this->db->quoteTableName($tableSchema->name)}")->queryScalar();
328+
}) + 1;
329+
}
330+
331+
return "ALTER SEQUENCE {$this->db->quoteColumnName($tableSchema->sequenceName)} RESTART WITH $value";
332+
}
311333
}

Schema.php

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
class Schema extends \yii\db\Schema
2929
{
3030

31-
private $_sequences = [];
3231
private $_lastInsertID = null;
3332

3433
/**
@@ -95,16 +94,6 @@ protected function loadTableSchema($name)
9594
$this->resolveTableNames($table, $name);
9695
if ($this->findColumns($table)) {
9796
$this->findConstraints($table);
98-
if (is_string($table->primaryKey) && isset($this->_sequences[$table->fullName . '.' . $table->primaryKey])) {
99-
$table->sequenceName = $this->_sequences[$table->fullName . '.' . $table->primaryKey];
100-
} elseif (is_array($table->primaryKey)) {
101-
foreach ($table->primaryKey as $pk) {
102-
if (isset($this->_sequences[$table->fullName . '.' . $pk])) {
103-
$table->sequenceName = $this->_sequences[$table->fullName . '.' . $pk];
104-
break;
105-
}
106-
}
107-
}
10897
return $table;
10998
}
11099
return null;
@@ -164,7 +153,7 @@ protected function findColumns($table)
164153
fld.rdb$field_precision AS fprecision,
165154
rel.rdb$null_flag AS fnull,
166155
fld.rdb$default_value AS fdefault_value,
167-
(SELECT 1 FROM RDB$TRIGGERS
156+
(SELECT RDB$TRIGGER_SOURCE FROM RDB$TRIGGERS
168157
WHERE RDB$SYSTEM_FLAG = 0
169158
AND UPPER(RDB$RELATION_NAME)=UPPER(\'' . $table->name . '\')
170159
AND RDB$TRIGGER_TYPE = 1
@@ -207,8 +196,13 @@ protected function findColumns($table)
207196
}
208197
foreach ($columns as $column) {
209198
$c = $this->loadColumnSchema($column);
210-
if ($c->autoIncrement) {
211-
$this->_sequences[$table->fullName . '.' . $c->name] = $table->fullName . '.' . $c->name;
199+
if ($table->sequenceName === null && $c->autoIncrement) {
200+
$matches = [];
201+
if (preg_match("/NEW.{$c->name}\s*=\s*GEN_ID\((\w+)/i", $column['fautoinc'], $matches)) {
202+
$table->sequenceName = $matches[1];
203+
} elseif (preg_match("/NEW.{$c->name}\s*=\s*NEXT\s+VALUE\s+FOR\s+(\w+)/i", $column['fautoinc'], $matches)) {
204+
$table->sequenceName = $matches[1];
205+
}
212206
}
213207
$table->columns[$c->name] = $c;
214208
if ($c->isPrimaryKey) {
@@ -239,7 +233,7 @@ protected function loadColumnSchema($column)
239233
$c->name = strtolower(rtrim($column['fname']));
240234
$c->allowNull = $column['fnull'] !== '1';
241235
$c->isPrimaryKey = $column['fprimary'];
242-
$c->autoIncrement = $column['fautoinc'] === '1';
236+
$c->autoIncrement = (boolean)$column['fautoinc'];
243237

244238
$c->type = self::TYPE_STRING;
245239

@@ -345,7 +339,7 @@ protected function loadColumnSchema($column)
345339

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

445439
/**
446-
* Executes the INSERT command, returning primary key values.
447-
* @param string $table the table that new rows will be inserted into.
448-
* @param array $columns the column data (name => value) to be inserted into the table.
449-
* @return array primary key values or false if the command fails
450-
* @since 2.0.4
440+
* @inheritdoc
451441
*/
452442
public function insert($table, $columns)
453443
{
444+
$this->_lastInsertID = false;
445+
$params = [];
446+
$sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);
447+
$returnColumns = $this->getTableSchema($table)->primaryKey;
448+
if (!empty($returnColumns)) {
449+
$returning = [];
450+
foreach ((array)$returnColumns as $name) {
451+
$returning[] = $this->quoteColumnName($name);
452+
}
453+
$sql .= ' RETURNING ' . implode(', ', $returning);
454+
}
454455

455-
$tableSchema = $this->getTableSchema($table);
456-
457-
$command = $this->db->createCommand()->insert($table, $columns);
456+
$command = $this->db->createCommand($sql, $params);
457+
$command->prepare(false);
458+
$result = $command->queryOne();
458459

459-
if ($tableSchema->sequenceName !== null) {
460-
$this->_lastInsertID = $command->queryScalar();
461-
if ($this->_lastInsertID === false) {
462-
return false;
463-
}
460+
if (!$command->pdoStatement->rowCount()) {
461+
return false;
464462
} else {
465-
if (!$command->execute()) {
466-
return false;
467-
}
468-
}
469-
$result = [];
470-
foreach ($tableSchema->primaryKey as $name) {
471-
if ($tableSchema->columns[$name]->autoIncrement) {
472-
$result[$name] = $this->getLastInsertID($tableSchema->sequenceName);
473-
break;
474-
} else {
475-
$result[$name] = isset($columns[$name]) ? $columns[$name] : $tableSchema->columns[$name]->defaultValue;
463+
if (!empty($returnColumns)) {
464+
foreach ((array)$returnColumns as $name) {
465+
if ($this->getTableSchema($table)->getColumn($name)->autoIncrement) {
466+
$this->_lastInsertID = $result[$name];
467+
break;
468+
}
469+
}
476470
}
471+
return $result;
477472
}
478-
return $result;
479473
}
480474

481475
/**
482-
* Returns the ID of the last inserted row or sequence value.
483-
* @param string $sequenceName name of the sequence object (required by some DBMS)
484-
* @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
485-
* @throws InvalidCallException if the DB connection is not active
486-
* @see http://www.php.net/manual/en/function.PDO-lastInsertId.php
476+
* @inheritdoc
487477
*/
488478
public function getLastInsertID($sequenceName = '')
489479
{
490480
if (!$this->db->isActive) {
491481
throw new InvalidCallException('DB Connection is not active.');
492482
}
483+
484+
if ($sequenceName !== '') {
485+
return $this->db->createCommand('SELECT GEN_ID('. $this->db->quoteTableName($sequenceName) . ', 0 ) FROM RDB$DATABASE;')->queryScalar();
486+
}
493487

494488
if ($this->_lastInsertID !== false) {
495489
return $this->_lastInsertID;

tests/QueryBuilderTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,47 @@ public function testAlterColumn()
217217
$this->assertEquals(false, $newColumns['profile_id']->allowNull);
218218
$this->assertEquals(0, $newColumns['profile_id']->defaultValue);
219219
}
220+
221+
public function testDropIndex()
222+
{
223+
$connection = $this->getConnection(true);
224+
$qb = $this->getQueryBuilder();
225+
226+
$this->assertEquals('DROP INDEX idx_int_col', $qb->dropIndex('idx_int_col', 'type'));
227+
228+
$columns = $connection->getTableSchema('type', true)->columnNames;
229+
230+
foreach ($columns as $column) {
231+
$result = $connection->createCommand($qb->createIndex('idx_' .$column, 'type', $column))->execute();
232+
}
233+
234+
foreach ($columns as $column) {
235+
$result = $connection->createCommand($qb->dropIndex('idx_' .$column, 'type'))->execute();
236+
}
237+
}
238+
239+
public function testResetSequence()
240+
{
241+
$connection = $this->getConnection(true);
242+
$qb = $this->getQueryBuilder();
243+
244+
$this->assertEquals('ALTER SEQUENCE seq_animal_id RESTART WITH 3', $qb->resetSequence('animal'));
245+
$this->assertEquals('ALTER SEQUENCE seq_animal_id RESTART WITH 10', $qb->resetSequence('animal', 10));
246+
247+
$this->assertEquals('ALTER SEQUENCE gen_profile_id RESTART WITH 3', $qb->resetSequence('profile'));
248+
$this->assertEquals('ALTER SEQUENCE gen_profile_id RESTART WITH 10', $qb->resetSequence('profile', 10));
249+
250+
$this->assertEquals(2, (new Query())->from('profile')->max('id', $connection));
251+
252+
$connection->createCommand()->insert('profile', ['description' => 'profile customer 3'])->execute();
253+
$this->assertEquals(3, (new Query())->from('profile')->max('id', $connection));
254+
255+
$connection->createCommand($qb->resetSequence('profile'))->execute();
256+
$connection->createCommand()->insert('profile', ['description' => 'profile customer 4'])->execute();
257+
$this->assertEquals(4, (new Query())->from('profile')->max('id', $connection));
258+
259+
$connection->createCommand($qb->resetSequence('profile', 10))->execute();
260+
$connection->createCommand()->insert('profile', ['description' => 'profile customer 11'])->execute();
261+
$this->assertEquals(11, (new Query())->from('profile')->max('id', $connection));
262+
}
220263
}

tests/SchemaTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,13 @@ public function testGetPDOType()
119119
}
120120
fclose($fp);
121121
}
122+
123+
public function testGetLastInsertID()
124+
{
125+
/* @var $schema Schema */
126+
$schema = $this->getConnection()->schema;
127+
$this->assertEquals(null, $schema->getLastInsertID());
128+
$this->assertEquals(2, $schema->getLastInsertID($schema->getTableSchema('animal')->sequenceName));
129+
$this->assertEquals(2, $schema->getLastInsertID($schema->getTableSchema('profile')->sequenceName));
130+
}
122131
}

tests/data/source.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ END;
9999
-- SQL
100100
EXECUTE block AS
101101
BEGIN
102-
IF (EXISTS(SELECT 1 FROM rdb$generators WHERE LOWER(rdb$generator_name) = 'gen_animal_id')) THEN
103-
EXECUTE STATEMENT 'DROP GENERATOR gen_animal_id;';
102+
IF (EXISTS(SELECT 1 FROM rdb$generators WHERE LOWER(rdb$generator_name) = 'seq_animal_id')) THEN
103+
EXECUTE STATEMENT 'DROP GENERATOR seq_animal_id;';
104104
END;
105105
-- SQL
106106
EXECUTE block AS
@@ -307,13 +307,13 @@ CREATE TABLE animal (
307307
PRIMARY KEY (id)
308308
);
309309
-- SQL
310-
CREATE GENERATOR gen_animal_id;
310+
CREATE SEQUENCE seq_animal_id;
311311
-- SQL
312312
CREATE TRIGGER tr_animal FOR animal
313313
ACTIVE BEFORE INSERT POSITION 0
314314
AS
315315
BEGIN
316-
if (NEW.ID is NULL) then NEW.ID = GEN_ID(gen_animal_id, 1);
316+
if (NEW.ID is NULL) then NEW.ID = NEXT VALUE FOR seq_animal_id;
317317
END
318318
-- SQL
319319
CREATE TABLE default_pk (

0 commit comments

Comments
 (0)