Fixes #14289: Added yii\db\Command::executeResetSequence() to work with Oracle

This commit is contained in:
CedricYii
2018-06-27 21:29:50 +02:00
committed by Alexander Makarov
parent 68e5a9b315
commit 3555633223
6 changed files with 59 additions and 19 deletions

View File

@ -4,6 +4,7 @@ Yii Framework 2 Change Log
2.0.16 under development 2.0.16 under development
------------------------ ------------------------
- Enh #14289: Added `yii\db\Command::executeResetSequence()` to work with Oracle (CedricYii)
- Enh #9133: Added `yii\behaviors\OptimisticLockBehavior` (tunecino) - Enh #9133: Added `yii\behaviors\OptimisticLockBehavior` (tunecino)
- Bug #16104: Fixed `yii\db\pgsql\QueryBuilder::dropIndex()` to prepend index name with schema name (wapmorgan) - Bug #16104: Fixed `yii\db\pgsql\QueryBuilder::dropIndex()` to prepend index name with schema name (wapmorgan)
- Bug #16193: Fixed `yii\filters\Cors` to not reflect origin header value when configured to wildcard origins (Jianjun Chen) - Bug #16193: Fixed `yii\filters\Cors` to not reflect origin header value when configured to wildcard origins (Jianjun Chen)

View File

@ -931,10 +931,10 @@ class Command extends Component
/** /**
* Creates a SQL command for resetting the sequence value of a table's primary key. * Creates a SQL command for resetting the sequence value of a table's primary key.
* The sequence will be reset such that the primary key of the next new row inserted * The sequence will be reset such that the primary key of the next new row inserted
* will have the specified value or 1. * will have the specified value or the maximum existing value +1.
* @param string $table the name of the table whose primary key sequence will be reset * @param string $table the name of the table whose primary key sequence will be reset
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set, * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
* the next new row's primary key will have a value 1. * the next new row's primary key will have the maximum existing value +1.
* @return $this the command object itself * @return $this the command object itself
* @throws NotSupportedException if this is not supported by the underlying DBMS * @throws NotSupportedException if this is not supported by the underlying DBMS
*/ */
@ -945,6 +945,22 @@ class Command extends Component
return $this->setSql($sql); return $this->setSql($sql);
} }
/**
* Executes a db command resetting the sequence value of a table's primary key.
* Reason for execute is that some databases (Oracle) need several queries to do so.
* The sequence is reset such that the primary key of the next new row inserted
* will have the specified value or the maximum existing value +1.
* @param string $table the name of the table whose primary key sequence is reset
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
* the next new row's primary key will have the maximum existing value +1.
* @throws NotSupportedException if this is not supported by the underlying DBMS
* @since 2.0.16
*/
public function executeResetSequence($table, $value = null)
{
return $this->db->getQueryBuilder()->executeResetSequence($table, $value);
}
/** /**
* Builds a SQL command for enabling or disabling integrity check. * Builds a SQL command for enabling or disabling integrity check.
* @param bool $check whether to turn on or off the integrity check. * @param bool $check whether to turn on or off the integrity check.

View File

@ -1028,10 +1028,10 @@ class QueryBuilder extends \yii\base\BaseObject
/** /**
* Creates a SQL statement for resetting the sequence value of a table's primary key. * Creates a SQL statement for resetting the sequence value of a table's primary key.
* The sequence will be reset such that the primary key of the next new row inserted * The sequence will be reset such that the primary key of the next new row inserted
* will have the specified value or 1. * will have the specified value or the maximum existing value +1.
* @param string $table the name of the table whose primary key sequence will be reset * @param string $table the name of the table whose primary key sequence will be reset
* @param array|string $value the value for the primary key of the next new row inserted. If this is not set, * @param array|string $value the value for the primary key of the next new row inserted. If this is not set,
* the next new row's primary key will have a value 1. * the next new row's primary key will have the maximum existing value +1.
* @return string the SQL statement for resetting sequence * @return string the SQL statement for resetting sequence
* @throws NotSupportedException if this is not supported by the underlying DBMS * @throws NotSupportedException if this is not supported by the underlying DBMS
*/ */
@ -1040,6 +1040,22 @@ class QueryBuilder extends \yii\base\BaseObject
throw new NotSupportedException($this->db->getDriverName() . ' does not support resetting sequence.'); throw new NotSupportedException($this->db->getDriverName() . ' does not support resetting sequence.');
} }
/**
* Execute a SQL statement for resetting the sequence value of a table's primary key.
* Reason for execute is that some databases (Oracle) need several queries to do so.
* The sequence is reset such that the primary key of the next new row inserted
* will have the specified value or the maximum existing value +1.
* @param string $table the name of the table whose primary key sequence is reset
* @param array|string $value the value for the primary key of the next new row inserted. If this is not set,
* the next new row's primary key will have the maximum existing value +1.
* @throws NotSupportedException if this is not supported by the underlying DBMS
* @since 2.0.16
*/
public function executeResetSequence($table, $value = null)
{
$this->db->createCommand()->resetSequence($table, $value)->execute();
}
/** /**
* Builds a SQL statement for enabling or disabling integrity check. * Builds a SQL statement for enabling or disabling integrity check.
* @param bool $check whether to turn on or off the integrity check. * @param bool $check whether to turn on or off the integrity check.

View File

@ -138,27 +138,34 @@ EOD;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function resetSequence($table, $value = null) public function executeResetSequence($table, $value = null)
{ {
$tableSchema = $this->db->getTableSchema($table); $tableSchema = $this->db->getTableSchema($table);
if ($tableSchema === null) { if ($tableSchema === null) {
throw new InvalidArgumentException("Unknown table: $table"); throw new InvalidArgumentException("Unknown table: $table");
} }
if ($tableSchema->sequenceName === null) { if ($tableSchema->sequenceName === null) {
return ''; throw new InvalidArgumentException("There is no sequence associated with table: $table");
} }
if ($value !== null) { if ($value !== null) {
$value = (int) $value; $value = (int) $value;
} else { } else {
if (count($tableSchema->primaryKey)>1) {
throw new InvalidArgumentException("Can't reset sequence for composite primary key in table: $table");
}
// use master connection to get the biggest PK value // use master connection to get the biggest PK value
$value = $this->db->useMaster(function (Connection $db) use ($tableSchema) { $value = $this->db->useMaster(function (Connection $db) use ($tableSchema) {
return $db->createCommand("SELECT MAX(\"{$tableSchema->primaryKey}\") FROM \"{$tableSchema->name}\"")->queryScalar(); return $db->createCommand(
'SELECT MAX("' . $tableSchema->primaryKey[0] . '") FROM "'. $tableSchema->name . '"'
)->queryScalar();
}) + 1; }) + 1;
} }
return "DROP SEQUENCE \"{$tableSchema->name}_SEQ\";" //Oracle needs at least two queries to reset sequence (see adding transactions and/or use alter method to avoid grants' issue?)
. "CREATE SEQUENCE \"{$tableSchema->name}_SEQ\" START WITH {$value} INCREMENT BY 1 NOMAXVALUE NOCACHE"; $this->db->createCommand('DROP SEQUENCE "' . $tableSchema->sequenceName . '"')->execute();
$this->db->createCommand('CREATE SEQUENCE "' . $tableSchema->sequenceName . '" START WITH ' . $value
. ' INCREMENT BY 1 NOMAXVALUE NOCACHE')->execute();
} }
/** /**

View File

@ -125,7 +125,7 @@ class ActiveFixture extends BaseActiveFixture
$table = $this->getTableSchema(); $table = $this->getTableSchema();
$this->db->createCommand()->delete($table->fullName)->execute(); $this->db->createCommand()->delete($table->fullName)->execute();
if ($table->sequenceName !== null) { if ($table->sequenceName !== null) {
$this->db->createCommand()->resetSequence($table->fullName, 1)->execute(); $this->db->createCommand()->executeResetSequence($table->fullName, 1);
} }
} }

View File

@ -106,19 +106,19 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest
$this->assertEquals($this->replaceQuotes($expected), $sql); $this->assertEquals($this->replaceQuotes($expected), $sql);
} }
public function testResetSequence() public function testExecuteResetSequence()
{ {
$db = $this->getConnection();
$qb = $this->getQueryBuilder(); $qb = $this->getQueryBuilder();
$sqlResult = "SELECT last_number FROM user_sequences WHERE sequence_name = 'item_SEQ'";
$expected = 'DROP SEQUENCE "item_SEQ";' $qb->executeResetSequence('item');
. 'CREATE SEQUENCE "item_SEQ" START WITH 6 INCREMENT BY 1 NOMAXVALUE NOCACHE'; $result = $db->createCommand($sqlResult)->queryScalar();
$sql = $qb->resetSequence('item'); $this->assertEquals(6, $result);
$this->assertEquals($expected, $sql);
$expected = 'DROP SEQUENCE "item_SEQ";' $qb->executeResetSequence('item', 4);
. 'CREATE SEQUENCE "item_SEQ" START WITH 4 INCREMENT BY 1 NOMAXVALUE NOCACHE'; $result = $db->createCommand($sqlResult)->queryScalar();
$sql = $qb->resetSequence('item', 4); $this->assertEquals(4, $result);
$this->assertEquals($expected, $sql);
} }
public function likeConditionProvider() public function likeConditionProvider()