* $q = new Doctrine_Query();
* $e = $q->expr;
* $q->select('*')->from('table')
* ->where($e->eq('id', $e->not('null'));
*
*
* @param string $expression
*
* @return string The logical expression.
*/
public function getNotExpression($expression)
{
return 'NOT(' . $expression . ')';
}
/**
* Returns the SQL that checks if an expression is null.
*
* @param string $expression The expression that should be compared to null.
*
* @return string The logical expression.
*/
public function getIsNullExpression($expression)
{
return $expression . ' IS NULL';
}
/**
* Returns the SQL that checks if an expression is not null.
*
* @param string $expression The expression that should be compared to null.
*
* @return string The logical expression.
*/
public function getIsNotNullExpression($expression)
{
return $expression . ' IS NOT NULL';
}
/**
* Returns the SQL that checks if an expression evaluates to a value between two values.
*
* The parameter $expression is checked if it is between $value1 and $value2.
*
* Note: There is a slight difference in the way BETWEEN works on some databases.
* http://www.w3schools.com/sql/sql_between.asp. If you want complete database
* independence you should avoid using between().
*
* @param string $expression The value to compare to.
* @param string $value1 The lower value to compare with.
* @param string $value2 The higher value to compare with.
*
* @return string The logical expression.
*/
public function getBetweenExpression($expression, $value1, $value2)
{
return $expression . ' BETWEEN ' . $value1 . ' AND ' . $value2;
}
/**
* Returns the SQL to get the arccosine of a value.
*
* @param string $value
*
* @return string
*/
public function getAcosExpression($value)
{
return 'ACOS(' . $value . ')';
}
/**
* Returns the SQL to get the sine of a value.
*
* @param string $value
*
* @return string
*/
public function getSinExpression($value)
{
return 'SIN(' . $value . ')';
}
/**
* Returns the SQL to get the PI value.
*
* @return string
*/
public function getPiExpression()
{
return 'PI()';
}
/**
* Returns the SQL to get the cosine of a value.
*
* @param string $value
*
* @return string
*/
public function getCosExpression($value)
{
return 'COS(' . $value . ')';
}
/**
* Returns the SQL to calculate the difference in days between the two passed dates.
*
* Computes diff = date1 - date2.
*
* @param string $date1
* @param string $date2
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateDiffExpression($date1, $date2)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Returns the SQL to add the number of given seconds to a date.
*
* @param string $date
* @param int $seconds
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateAddSecondsExpression($date, $seconds)
{
return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND);
}
/**
* Returns the SQL to subtract the number of given seconds from a date.
*
* @param string $date
* @param int $seconds
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateSubSecondsExpression($date, $seconds)
{
return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND);
}
/**
* Returns the SQL to add the number of given minutes to a date.
*
* @param string $date
* @param int $minutes
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateAddMinutesExpression($date, $minutes)
{
return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE);
}
/**
* Returns the SQL to subtract the number of given minutes from a date.
*
* @param string $date
* @param int $minutes
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateSubMinutesExpression($date, $minutes)
{
return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE);
}
/**
* Returns the SQL to add the number of given hours to a date.
*
* @param string $date
* @param int $hours
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateAddHourExpression($date, $hours)
{
return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR);
}
/**
* Returns the SQL to subtract the number of given hours to a date.
*
* @param string $date
* @param int $hours
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateSubHourExpression($date, $hours)
{
return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR);
}
/**
* Returns the SQL to add the number of given days to a date.
*
* @param string $date
* @param int $days
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateAddDaysExpression($date, $days)
{
return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY);
}
/**
* Returns the SQL to subtract the number of given days to a date.
*
* @param string $date
* @param int $days
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateSubDaysExpression($date, $days)
{
return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY);
}
/**
* Returns the SQL to add the number of given weeks to a date.
*
* @param string $date
* @param int $weeks
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateAddWeeksExpression($date, $weeks)
{
return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK);
}
/**
* Returns the SQL to subtract the number of given weeks from a date.
*
* @param string $date
* @param int $weeks
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateSubWeeksExpression($date, $weeks)
{
return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK);
}
/**
* Returns the SQL to add the number of given months to a date.
*
* @param string $date
* @param int $months
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateAddMonthExpression($date, $months)
{
return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH);
}
/**
* Returns the SQL to subtract the number of given months to a date.
*
* @param string $date
* @param int $months
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateSubMonthExpression($date, $months)
{
return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH);
}
/**
* Returns the SQL to add the number of given quarters to a date.
*
* @param string $date
* @param int $quarters
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateAddQuartersExpression($date, $quarters)
{
return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER);
}
/**
* Returns the SQL to subtract the number of given quarters from a date.
*
* @param string $date
* @param int $quarters
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateSubQuartersExpression($date, $quarters)
{
return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER);
}
/**
* Returns the SQL to add the number of given years to a date.
*
* @param string $date
* @param int $years
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateAddYearsExpression($date, $years)
{
return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR);
}
/**
* Returns the SQL to subtract the number of given years from a date.
*
* @param string $date
* @param int $years
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateSubYearsExpression($date, $years)
{
return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR);
}
/**
* Returns the SQL for a date arithmetic expression.
*
* @param string $date The column or literal representing a date to perform the arithmetic operation on.
* @param string $operator The arithmetic operator (+ or -).
* @param int $interval The interval that shall be calculated into the date.
* @param string $unit The unit of the interval that shall be calculated into the date.
* One of the DATE_INTERVAL_UNIT_* constants.
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Returns the SQL bit AND comparison expression.
*
* @param string $value1
* @param string $value2
*
* @return string
*/
public function getBitAndComparisonExpression($value1, $value2)
{
return '(' . $value1 . ' & ' . $value2 . ')';
}
/**
* Returns the SQL bit OR comparison expression.
*
* @param string $value1
* @param string $value2
*
* @return string
*/
public function getBitOrComparisonExpression($value1, $value2)
{
return '(' . $value1 . ' | ' . $value2 . ')';
}
/**
* Returns the FOR UPDATE expression.
*
* @return string
*/
public function getForUpdateSQL()
{
return 'FOR UPDATE';
}
/**
* Honors that some SQL vendors such as MsSql use table hints for locking instead of the
* ANSI SQL FOR UPDATE specification.
*
* @param string $fromClause The FROM clause to append the hint for the given lock mode to.
* @param int|null $lockMode One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will
* be appended to the FROM clause.
*
* @return string
*/
public function appendLockHint($fromClause, $lockMode)
{
return $fromClause;
}
/**
* Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
*
* This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
* vendors allow to lighten this constraint up to be a real read lock.
*
* @return string
*/
public function getReadLockSQL()
{
return $this->getForUpdateSQL();
}
/**
* Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
*
* The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
*
* @return string
*/
public function getWriteLockSQL()
{
return $this->getForUpdateSQL();
}
/**
* Returns the SQL snippet to drop an existing database.
*
* @param string $database The name of the database that should be dropped.
*
* @return string
*/
public function getDropDatabaseSQL($database)
{
return 'DROP DATABASE ' . $database;
}
/**
* Returns the SQL snippet to drop an existing table.
*
* @param Table|string $table
*
* @return string
*
* @throws InvalidArgumentException
*/
public function getDropTableSQL($table)
{
$tableArg = $table;
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
}
if (! is_string($table)) {
throw new InvalidArgumentException(
__METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.'
);
}
if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
$eventArgs = new SchemaDropTableEventArgs($tableArg, $this);
$this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs);
if ($eventArgs->isDefaultPrevented()) {
$sql = $eventArgs->getSql();
if ($sql === null) {
throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL');
}
return $sql;
}
}
return 'DROP TABLE ' . $table;
}
/**
* Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
*
* @param Table|string $table
*
* @return string
*/
public function getDropTemporaryTableSQL($table)
{
return $this->getDropTableSQL($table);
}
/**
* Returns the SQL to drop an index from a table.
*
* @param Index|string $index
* @param Table|string $table
*
* @return string
*
* @throws InvalidArgumentException
*/
public function getDropIndexSQL($index, $table = null)
{
if ($index instanceof Index) {
$index = $index->getQuotedName($this);
} elseif (! is_string($index)) {
throw new InvalidArgumentException(
__METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.'
);
}
return 'DROP INDEX ' . $index;
}
/**
* Returns the SQL to drop a constraint.
*
* @param Constraint|string $constraint
* @param Table|string $table
*
* @return string
*/
public function getDropConstraintSQL($constraint, $table)
{
if (! $constraint instanceof Constraint) {
$constraint = new Identifier($constraint);
}
if (! $table instanceof Table) {
$table = new Identifier($table);
}
$constraint = $constraint->getQuotedName($this);
$table = $table->getQuotedName($this);
return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint;
}
/**
* Returns the SQL to drop a foreign key.
*
* @param ForeignKeyConstraint|string $foreignKey
* @param Table|string $table
*
* @return string
*/
public function getDropForeignKeySQL($foreignKey, $table)
{
if (! $foreignKey instanceof ForeignKeyConstraint) {
$foreignKey = new Identifier($foreignKey);
}
if (! $table instanceof Table) {
$table = new Identifier($table);
}
$foreignKey = $foreignKey->getQuotedName($this);
$table = $table->getQuotedName($this);
return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey;
}
/**
* Returns the SQL statement(s) to create a table with the specified name, columns and constraints
* on this platform.
*
* @param int $createFlags
*
* @return string[] The sequence of SQL statements.
*
* @throws DBALException
* @throws InvalidArgumentException
*/
public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES)
{
if (! is_int($createFlags)) {
throw new InvalidArgumentException(
'Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.'
);
}
if (count($table->getColumns()) === 0) {
throw DBALException::noColumnsSpecifiedForTable($table->getName());
}
$tableName = $table->getQuotedName($this);
$options = $table->getOptions();
$options['uniqueConstraints'] = [];
$options['indexes'] = [];
$options['primary'] = [];
if (($createFlags & self::CREATE_INDEXES) > 0) {
foreach ($table->getIndexes() as $index) {
if ($index->isPrimary()) {
$options['primary'] = $index->getQuotedColumns($this);
$options['primary_index'] = $index;
} else {
$options['indexes'][$index->getQuotedName($this)] = $index;
}
}
}
$columnSql = [];
$columns = [];
foreach ($table->getColumns() as $column) {
if (
$this->_eventManager !== null
&& $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)
) {
$eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
$this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
$columnSql = array_merge($columnSql, $eventArgs->getSql());
if ($eventArgs->isDefaultPrevented()) {
continue;
}
}
$columnData = array_merge($column->toArray(), [
'name' => $column->getQuotedName($this),
'version' => $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false,
'comment' => $this->getColumnComment($column),
]);
if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) {
$columnData['length'] = 255;
}
if (in_array($column->getName(), $options['primary'])) {
$columnData['primary'] = true;
}
$columns[$columnData['name']] = $columnData;
}
if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) {
$options['foreignKeys'] = [];
foreach ($table->getForeignKeys() as $fkConstraint) {
$options['foreignKeys'][] = $fkConstraint;
}
}
if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
$eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
$this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
if ($eventArgs->isDefaultPrevented()) {
return array_merge($eventArgs->getSql(), $columnSql);
}
}
$sql = $this->_getCreateTableSQL($tableName, $columns, $options);
if ($this->supportsCommentOnStatement()) {
if ($table->hasOption('comment')) {
$sql[] = $this->getCommentOnTableSQL($tableName, $table->getOption('comment'));
}
foreach ($table->getColumns() as $column) {
$comment = $this->getColumnComment($column);
if ($comment === null || $comment === '') {
continue;
}
$sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment);
}
}
return array_merge($sql, $columnSql);
}
protected function getCommentOnTableSQL(string $tableName, ?string $comment): string
{
$tableName = new Identifier($tableName);
return sprintf(
'COMMENT ON TABLE %s IS %s',
$tableName->getQuotedName($this),
$this->quoteStringLiteral((string) $comment)
);
}
/**
* @param string $tableName
* @param string $columnName
* @param string|null $comment
*
* @return string
*/
public function getCommentOnColumnSQL($tableName, $columnName, $comment)
{
$tableName = new Identifier($tableName);
$columnName = new Identifier($columnName);
return sprintf(
'COMMENT ON COLUMN %s.%s IS %s',
$tableName->getQuotedName($this),
$columnName->getQuotedName($this),
$this->quoteStringLiteral((string) $comment)
);
}
/**
* Returns the SQL to create inline comment on a column.
*
* @param string $comment
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getInlineColumnCommentSQL($comment)
{
if (! $this->supportsInlineColumnComments()) {
throw DBALException::notSupported(__METHOD__);
}
return 'COMMENT ' . $this->quoteStringLiteral($comment);
}
/**
* Returns the SQL used to create a table.
*
* @param string $name
* @param mixed[][] $columns
* @param mixed[] $options
*
* @return string[]
*/
protected function _getCreateTableSQL($name, array $columns, array $options = [])
{
$columnListSql = $this->getColumnDeclarationListSQL($columns);
if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
foreach ($options['uniqueConstraints'] as $index => $definition) {
$columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition);
}
}
if (isset($options['primary']) && ! empty($options['primary'])) {
$columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
}
if (isset($options['indexes']) && ! empty($options['indexes'])) {
foreach ($options['indexes'] as $index => $definition) {
$columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
}
}
$query = 'CREATE TABLE ' . $name . ' (' . $columnListSql;
$check = $this->getCheckDeclarationSQL($columns);
if (! empty($check)) {
$query .= ', ' . $check;
}
$query .= ')';
$sql[] = $query;
if (isset($options['foreignKeys'])) {
foreach ((array) $options['foreignKeys'] as $definition) {
$sql[] = $this->getCreateForeignKeySQL($definition, $name);
}
}
return $sql;
}
/**
* @return string
*/
public function getCreateTemporaryTableSnippetSQL()
{
return 'CREATE TEMPORARY TABLE';
}
/**
* Returns the SQL to create a sequence on this platform.
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getCreateSequenceSQL(Sequence $sequence)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Returns the SQL to change a sequence on this platform.
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getAlterSequenceSQL(Sequence $sequence)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Returns the SQL to create a constraint on a table on this platform.
*
* @param Table|string $table
*
* @return string
*
* @throws InvalidArgumentException
*/
public function getCreateConstraintSQL(Constraint $constraint, $table)
{
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
}
$query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this);
$columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
$referencesClause = '';
if ($constraint instanceof Index) {
if ($constraint->isPrimary()) {
$query .= ' PRIMARY KEY';
} elseif ($constraint->isUnique()) {
$query .= ' UNIQUE';
} else {
throw new InvalidArgumentException(
'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
);
}
} elseif ($constraint instanceof ForeignKeyConstraint) {
$query .= ' FOREIGN KEY';
$referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
}
$query .= ' ' . $columnList . $referencesClause;
return $query;
}
/**
* Returns the SQL to create an index on a table on this platform.
*
* @param Table|string $table The name of the table on which the index is to be created.
*
* @return string
*
* @throws InvalidArgumentException
*/
public function getCreateIndexSQL(Index $index, $table)
{
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
}
$name = $index->getQuotedName($this);
$columns = $index->getColumns();
if (count($columns) === 0) {
throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
}
if ($index->isPrimary()) {
return $this->getCreatePrimaryKeySQL($index, $table);
}
$query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
$query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
return $query;
}
/**
* Adds condition for partial index.
*
* @return string
*/
protected function getPartialIndexSQL(Index $index)
{
if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
return ' WHERE ' . $index->getOption('where');
}
return '';
}
/**
* Adds additional flags for index generation.
*
* @return string
*/
protected function getCreateIndexSQLFlags(Index $index)
{
return $index->isUnique() ? 'UNIQUE ' : '';
}
/**
* Returns the SQL to create an unnamed primary key constraint.
*
* @param Table|string $table
*
* @return string
*/
public function getCreatePrimaryKeySQL(Index $index, $table)
{
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
}
return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
}
/**
* Returns the SQL to create a named schema.
*
* @param string $schemaName
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getCreateSchemaSQL($schemaName)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Quotes a string so that it can be safely used as a table or column name,
* even if it is a reserved word of the platform. This also detects identifier
* chains separated by dot and quotes them independently.
*
* NOTE: Just because you CAN use quoted identifiers doesn't mean
* you SHOULD use them. In general, they end up causing way more
* problems than they solve.
*
* @param string $str The identifier name to be quoted.
*
* @return string The quoted identifier string.
*/
public function quoteIdentifier($str)
{
if (strpos($str, '.') !== false) {
$parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $str));
return implode('.', $parts);
}
return $this->quoteSingleIdentifier($str);
}
/**
* Quotes a single identifier (no dot chain separation).
*
* @param string $str The identifier name to be quoted.
*
* @return string The quoted identifier string.
*/
public function quoteSingleIdentifier($str)
{
$c = $this->getIdentifierQuoteCharacter();
return $c . str_replace($c, $c . $c, $str) . $c;
}
/**
* Returns the SQL to create a new foreign key.
*
* @param ForeignKeyConstraint $foreignKey The foreign key constraint.
* @param Table|string $table The name of the table on which the foreign key is to be created.
*
* @return string
*/
public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
{
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
}
return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey);
}
/**
* Gets the SQL statements for altering an existing table.
*
* This method returns an array of SQL statements, since some platforms need several statements.
*
* @return string[]
*
* @throws DBALException If not supported on this platform.
*/
public function getAlterTableSQL(TableDiff $diff)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @param mixed[] $columnSql
*
* @return bool
*/
protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql)
{
if ($this->_eventManager === null) {
return false;
}
if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
return false;
}
$eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this);
$this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs);
$columnSql = array_merge($columnSql, $eventArgs->getSql());
return $eventArgs->isDefaultPrevented();
}
/**
* @param string[] $columnSql
*
* @return bool
*/
protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql)
{
if ($this->_eventManager === null) {
return false;
}
if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
return false;
}
$eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this);
$this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs);
$columnSql = array_merge($columnSql, $eventArgs->getSql());
return $eventArgs->isDefaultPrevented();
}
/**
* @param string[] $columnSql
*
* @return bool
*/
protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql)
{
if ($this->_eventManager === null) {
return false;
}
if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
return false;
}
$eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this);
$this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs);
$columnSql = array_merge($columnSql, $eventArgs->getSql());
return $eventArgs->isDefaultPrevented();
}
/**
* @param string $oldColumnName
* @param string[] $columnSql
*
* @return bool
*/
protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql)
{
if ($this->_eventManager === null) {
return false;
}
if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
return false;
}
$eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this);
$this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs);
$columnSql = array_merge($columnSql, $eventArgs->getSql());
return $eventArgs->isDefaultPrevented();
}
/**
* @param string[] $sql
*
* @return bool
*/
protected function onSchemaAlterTable(TableDiff $diff, &$sql)
{
if ($this->_eventManager === null) {
return false;
}
if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
return false;
}
$eventArgs = new SchemaAlterTableEventArgs($diff, $this);
$this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs);
$sql = array_merge($sql, $eventArgs->getSql());
return $eventArgs->isDefaultPrevented();
}
/**
* @return string[]
*/
protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
{
$tableName = $diff->getName($this)->getQuotedName($this);
$sql = [];
if ($this->supportsForeignKeyConstraints()) {
foreach ($diff->removedForeignKeys as $foreignKey) {
$sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
}
foreach ($diff->changedForeignKeys as $foreignKey) {
$sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
}
}
foreach ($diff->removedIndexes as $index) {
$sql[] = $this->getDropIndexSQL($index, $tableName);
}
foreach ($diff->changedIndexes as $index) {
$sql[] = $this->getDropIndexSQL($index, $tableName);
}
return $sql;
}
/**
* @return string[]
*/
protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
{
$sql = [];
$newName = $diff->getNewName();
if ($newName !== false) {
$tableName = $newName->getQuotedName($this);
} else {
$tableName = $diff->getName($this)->getQuotedName($this);
}
if ($this->supportsForeignKeyConstraints()) {
foreach ($diff->addedForeignKeys as $foreignKey) {
$sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
}
foreach ($diff->changedForeignKeys as $foreignKey) {
$sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
}
}
foreach ($diff->addedIndexes as $index) {
$sql[] = $this->getCreateIndexSQL($index, $tableName);
}
foreach ($diff->changedIndexes as $index) {
$sql[] = $this->getCreateIndexSQL($index, $tableName);
}
foreach ($diff->renamedIndexes as $oldIndexName => $index) {
$oldIndexName = new Identifier($oldIndexName);
$sql = array_merge(
$sql,
$this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName)
);
}
return $sql;
}
/**
* Returns the SQL for renaming an index on a table.
*
* @param string $oldIndexName The name of the index to rename from.
* @param Index $index The definition of the index to rename to.
* @param string $tableName The table to rename the given index on.
*
* @return string[] The sequence of SQL statements for renaming the given index.
*/
protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
{
return [
$this->getDropIndexSQL($oldIndexName, $tableName),
$this->getCreateIndexSQL($index, $tableName),
];
}
/**
* Common code for alter table statement generation that updates the changed Index and Foreign Key definitions.
*
* @deprecated
*
* @return string[]
*/
protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff)
{
return array_merge(
$this->getPreAlterTableIndexForeignKeySQL($diff),
$this->getPostAlterTableIndexForeignKeySQL($diff)
);
}
/**
* Gets declaration of a number of columns in bulk.
*
* @param mixed[][] $columns A multidimensional associative array.
* The first dimension determines the column name, while the second
* dimension is keyed with the name of the properties
* of the column being declared as array indexes. Currently, the types
* of supported column properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* column. If this argument is missing the column should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this column.
*
* notnull
* Boolean flag that indicates whether this column is constrained
* to not be set to null.
* charset
* Text value with the default CHARACTER SET for this column.
* collation
* Text value with the default COLLATION for this column.
* unique
* unique constraint
*
* @return string
*/
public function getColumnDeclarationListSQL(array $columns)
{
$declarations = [];
foreach ($columns as $name => $column) {
$declarations[] = $this->getColumnDeclarationSQL($name, $column);
}
return implode(', ', $declarations);
}
/**
* Obtains DBMS specific SQL code portion needed to declare a generic type
* column to be used in statements like CREATE TABLE.
*
* @param string $name The name the column to be declared.
* @param mixed[] $column An associative array with the name of the properties
* of the column being declared as array indexes. Currently, the types
* of supported column properties are as follows:
*
* length
* Integer value that determines the maximum length of the text
* column. If this argument is missing the column should be
* declared to have the longest length allowed by the DBMS.
*
* default
* Text value to be used as default for this column.
*
* notnull
* Boolean flag that indicates whether this column is constrained
* to not be set to null.
* charset
* Text value with the default CHARACTER SET for this column.
* collation
* Text value with the default COLLATION for this column.
* unique
* unique constraint
* check
* column check constraint
* columnDefinition
* a string that defines the complete column
*
* @return string DBMS specific SQL code portion that should be used to declare the column.
*/
public function getColumnDeclarationSQL($name, array $column)
{
if (isset($column['columnDefinition'])) {
$declaration = $this->getCustomTypeDeclarationSQL($column);
} else {
$default = $this->getDefaultValueDeclarationSQL($column);
$charset = isset($column['charset']) && $column['charset'] ?
' ' . $this->getColumnCharsetDeclarationSQL($column['charset']) : '';
$collation = isset($column['collation']) && $column['collation'] ?
' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : '';
$notnull = isset($column['notnull']) && $column['notnull'] ? ' NOT NULL' : '';
$unique = isset($column['unique']) && $column['unique'] ?
' ' . $this->getUniqueFieldDeclarationSQL() : '';
$check = isset($column['check']) && $column['check'] ?
' ' . $column['check'] : '';
$typeDecl = $column['type']->getSQLDeclaration($column, $this);
$declaration = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation;
if ($this->supportsInlineColumnComments() && isset($column['comment']) && $column['comment'] !== '') {
$declaration .= ' ' . $this->getInlineColumnCommentSQL($column['comment']);
}
}
return $name . ' ' . $declaration;
}
/**
* Returns the SQL snippet that declares a floating point column of arbitrary precision.
*
* @param mixed[] $column
*
* @return string
*/
public function getDecimalTypeDeclarationSQL(array $column)
{
$column['precision'] = ! isset($column['precision']) || empty($column['precision'])
? 10 : $column['precision'];
$column['scale'] = ! isset($column['scale']) || empty($column['scale'])
? 0 : $column['scale'];
return 'NUMERIC(' . $column['precision'] . ', ' . $column['scale'] . ')';
}
/**
* Obtains DBMS specific SQL code portion needed to set a default value
* declaration to be used in statements like CREATE TABLE.
*
* @param mixed[] $column The column definition array.
*
* @return string DBMS specific SQL code portion needed to set a default value.
*/
public function getDefaultValueDeclarationSQL($column)
{
if (! isset($column['default'])) {
return empty($column['notnull']) ? ' DEFAULT NULL' : '';
}
$default = $column['default'];
if (! isset($column['type'])) {
return " DEFAULT '" . $default . "'";
}
$type = $column['type'];
if ($type instanceof Types\PhpIntegerMappingType) {
return ' DEFAULT ' . $default;
}
if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
return ' DEFAULT ' . $this->getCurrentTimestampSQL();
}
if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
return ' DEFAULT ' . $this->getCurrentTimeSQL();
}
if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
return ' DEFAULT ' . $this->getCurrentDateSQL();
}
if ($type instanceof Types\BooleanType) {
return " DEFAULT '" . $this->convertBooleans($default) . "'";
}
return ' DEFAULT ' . $this->quoteStringLiteral($default);
}
/**
* Obtains DBMS specific SQL code portion needed to set a CHECK constraint
* declaration to be used in statements like CREATE TABLE.
*
* @param string[]|mixed[][] $definition The check definition.
*
* @return string DBMS specific SQL code portion needed to set a CHECK constraint.
*/
public function getCheckDeclarationSQL(array $definition)
{
$constraints = [];
foreach ($definition as $column => $def) {
if (is_string($def)) {
$constraints[] = 'CHECK (' . $def . ')';
} else {
if (isset($def['min'])) {
$constraints[] = 'CHECK (' . $column . ' >= ' . $def['min'] . ')';
}
if (isset($def['max'])) {
$constraints[] = 'CHECK (' . $column . ' <= ' . $def['max'] . ')';
}
}
}
return implode(', ', $constraints);
}
/**
* Obtains DBMS specific SQL code portion needed to set a unique
* constraint declaration to be used in statements like CREATE TABLE.
*
* @param string $name The name of the unique constraint.
* @param Index $index The index definition.
*
* @return string DBMS specific SQL code portion needed to set a constraint.
*
* @throws InvalidArgumentException
*/
public function getUniqueConstraintDeclarationSQL($name, Index $index)
{
$columns = $index->getColumns();
$name = new Identifier($name);
if (count($columns) === 0) {
throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
}
return 'CONSTRAINT ' . $name->getQuotedName($this) . ' UNIQUE ('
. $this->getIndexFieldDeclarationListSQL($index)
. ')' . $this->getPartialIndexSQL($index);
}
/**
* Obtains DBMS specific SQL code portion needed to set an index
* declaration to be used in statements like CREATE TABLE.
*
* @param string $name The name of the index.
* @param Index $index The index definition.
*
* @return string DBMS specific SQL code portion needed to set an index.
*
* @throws InvalidArgumentException
*/
public function getIndexDeclarationSQL($name, Index $index)
{
$columns = $index->getColumns();
$name = new Identifier($name);
if (count($columns) === 0) {
throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
}
return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' ('
. $this->getIndexFieldDeclarationListSQL($index)
. ')' . $this->getPartialIndexSQL($index);
}
/**
* Obtains SQL code portion needed to create a custom column,
* e.g. when a column has the "columnDefinition" keyword.
* Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
*
* @param mixed[] $column
*
* @return string
*/
public function getCustomTypeDeclarationSQL(array $column)
{
return $column['columnDefinition'];
}
/**
* Obtains DBMS specific SQL code portion needed to set an index
* declaration to be used in statements like CREATE TABLE.
*
* @param mixed[]|Index $columnsOrIndex array declaration is deprecated, prefer passing Index to this method
*/
public function getIndexFieldDeclarationListSQL($columnsOrIndex): string
{
if ($columnsOrIndex instanceof Index) {
return implode(', ', $columnsOrIndex->getQuotedColumns($this));
}
if (! is_array($columnsOrIndex)) {
throw new InvalidArgumentException('Fields argument should be an Index or array.');
}
$ret = [];
foreach ($columnsOrIndex as $column => $definition) {
if (is_array($definition)) {
$ret[] = $column;
} else {
$ret[] = $definition;
}
}
return implode(', ', $ret);
}
/**
* Returns the required SQL string that fits between CREATE ... TABLE
* to create the table as a temporary table.
*
* Should be overridden in driver classes to return the correct string for the
* specific database type.
*
* The default is to return the string "TEMPORARY" - this will result in a
* SQL error for any database that does not support temporary tables, or that
* requires a different SQL command from "CREATE TEMPORARY TABLE".
*
* @return string The string required to be placed between "CREATE" and "TABLE"
* to generate a temporary table, if possible.
*/
public function getTemporaryTableSQL()
{
return 'TEMPORARY';
}
/**
* Some vendors require temporary table names to be qualified specially.
*
* @param string $tableName
*
* @return string
*/
public function getTemporaryTableName($tableName)
{
return $tableName;
}
/**
* Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
* of a column declaration to be used in statements like CREATE TABLE.
*
* @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
* of a column declaration.
*/
public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
{
$sql = $this->getForeignKeyBaseDeclarationSQL($foreignKey);
$sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
return $sql;
}
/**
* Returns the FOREIGN KEY query section dealing with non-standard options
* as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
*
* @param ForeignKeyConstraint $foreignKey The foreign key definition.
*
* @return string
*/
public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
{
$query = '';
if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
$query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
}
if ($foreignKey->hasOption('onDelete')) {
$query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
}
return $query;
}
/**
* Returns the given referential action in uppercase if valid, otherwise throws an exception.
*
* @param string $action The foreign key referential action.
*
* @return string
*
* @throws InvalidArgumentException If unknown referential action given.
*/
public function getForeignKeyReferentialActionSQL($action)
{
$upper = strtoupper($action);
switch ($upper) {
case 'CASCADE':
case 'SET NULL':
case 'NO ACTION':
case 'RESTRICT':
case 'SET DEFAULT':
return $upper;
default:
throw new InvalidArgumentException('Invalid foreign key action: ' . $upper);
}
}
/**
* Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
* of a column declaration to be used in statements like CREATE TABLE.
*
* @return string
*
* @throws InvalidArgumentException
*/
public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
{
$sql = '';
if (strlen($foreignKey->getName())) {
$sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
}
$sql .= 'FOREIGN KEY (';
if (count($foreignKey->getLocalColumns()) === 0) {
throw new InvalidArgumentException("Incomplete definition. 'local' required.");
}
if (count($foreignKey->getForeignColumns()) === 0) {
throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
}
if (strlen($foreignKey->getForeignTableName()) === 0) {
throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
}
return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this))
. ') REFERENCES '
. $foreignKey->getQuotedForeignTableName($this) . ' ('
. implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')';
}
/**
* Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
* of a column declaration to be used in statements like CREATE TABLE.
*
* @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
* of a column declaration.
*/
public function getUniqueFieldDeclarationSQL()
{
return 'UNIQUE';
}
/**
* Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
* of a column declaration to be used in statements like CREATE TABLE.
*
* @param string $charset The name of the charset.
*
* @return string DBMS specific SQL code portion needed to set the CHARACTER SET
* of a column declaration.
*/
public function getColumnCharsetDeclarationSQL($charset)
{
return '';
}
/**
* Obtains DBMS specific SQL code portion needed to set the COLLATION
* of a column declaration to be used in statements like CREATE TABLE.
*
* @param string $collation The name of the collation.
*
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a column declaration.
*/
public function getColumnCollationDeclarationSQL($collation)
{
return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : '';
}
/**
* Whether the platform prefers sequences for ID generation.
* Subclasses should override this method to return TRUE if they prefer sequences.
*
* @return bool
*/
public function prefersSequences()
{
return false;
}
/**
* Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
* Subclasses should override this method to return TRUE if they prefer identity columns.
*
* @return bool
*/
public function prefersIdentityColumns()
{
return false;
}
/**
* Some platforms need the boolean values to be converted.
*
* The default conversion in this implementation converts to integers (false => 0, true => 1).
*
* Note: if the input is not a boolean the original input might be returned.
*
* There are two contexts when converting booleans: Literals and Prepared Statements.
* This method should handle the literal case
*
* @param mixed $item A boolean or an array of them.
*
* @return mixed A boolean database value or an array of them.
*/
public function convertBooleans($item)
{
if (is_array($item)) {
foreach ($item as $k => $value) {
if (! is_bool($value)) {
continue;
}
$item[$k] = (int) $value;
}
} elseif (is_bool($item)) {
$item = (int) $item;
}
return $item;
}
/**
* Some platforms have boolean literals that needs to be correctly converted
*
* The default conversion tries to convert value into bool "(bool)$item"
*
* @param mixed $item
*
* @return bool|null
*/
public function convertFromBoolean($item)
{
return $item === null ? null : (bool) $item;
}
/**
* This method should handle the prepared statements case. When there is no
* distinction, it's OK to use the same method.
*
* Note: if the input is not a boolean the original input might be returned.
*
* @param mixed $item A boolean or an array of them.
*
* @return mixed A boolean database value or an array of them.
*/
public function convertBooleansToDatabaseValue($item)
{
return $this->convertBooleans($item);
}
/**
* Returns the SQL specific for the platform to get the current date.
*
* @return string
*/
public function getCurrentDateSQL()
{
return 'CURRENT_DATE';
}
/**
* Returns the SQL specific for the platform to get the current time.
*
* @return string
*/
public function getCurrentTimeSQL()
{
return 'CURRENT_TIME';
}
/**
* Returns the SQL specific for the platform to get the current timestamp
*
* @return string
*/
public function getCurrentTimestampSQL()
{
return 'CURRENT_TIMESTAMP';
}
/**
* Returns the SQL for a given transaction isolation level Connection constant.
*
* @param int $level
*
* @return string
*
* @throws InvalidArgumentException
*/
protected function _getTransactionIsolationLevelSQL($level)
{
switch ($level) {
case TransactionIsolationLevel::READ_UNCOMMITTED:
return 'READ UNCOMMITTED';
case TransactionIsolationLevel::READ_COMMITTED:
return 'READ COMMITTED';
case TransactionIsolationLevel::REPEATABLE_READ:
return 'REPEATABLE READ';
case TransactionIsolationLevel::SERIALIZABLE:
return 'SERIALIZABLE';
default:
throw new InvalidArgumentException('Invalid isolation level:' . $level);
}
}
/**
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getListDatabasesSQL()
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Returns the SQL statement for retrieving the namespaces defined in the database.
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getListNamespacesSQL()
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @param string $database
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getListSequencesSQL($database)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @param string $table
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getListTableConstraintsSQL($table)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @param string $table
* @param string $database
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getListTableColumnsSQL($table, $database = null)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getListTablesSQL()
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getListUsersSQL()
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Returns the SQL to list all views of a database or user.
*
* @param string $database
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getListViewsSQL($database)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Returns the list of indexes for the current database.
*
* The current database parameter is optional but will always be passed
* when using the SchemaManager API and is the database the given table is in.
*
* Attention: Some platforms only support currentDatabase when they
* are connected with that database. Cross-database information schema
* requests may be impossible.
*
* @param string $table
* @param string $database
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getListTableIndexesSQL($table, $database = null)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @param string $table
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getListTableForeignKeysSQL($table)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @param string $name
* @param string $sql
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getCreateViewSQL($name, $sql)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @param string $name
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDropViewSQL($name)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Returns the SQL snippet to drop an existing sequence.
*
* @param Sequence|string $sequence
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDropSequenceSQL($sequence)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @param string $sequence
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getSequenceNextValSQL($sequence)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Returns the SQL to create a new database.
*
* @param string $database The name of the database that should be created.
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getCreateDatabaseSQL($database)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Returns the SQL to set the transaction isolation level.
*
* @param int $level
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getSetTransactionIsolationSQL($level)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Obtains DBMS specific SQL to be used to create datetime columns in
* statements like CREATE TABLE.
*
* @param mixed[] $column
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateTimeTypeDeclarationSQL(array $column)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Obtains DBMS specific SQL to be used to create datetime with timezone offset columns.
*
* @param mixed[] $column
*
* @return string
*/
public function getDateTimeTzTypeDeclarationSQL(array $column)
{
return $this->getDateTimeTypeDeclarationSQL($column);
}
/**
* Obtains DBMS specific SQL to be used to create date columns in statements
* like CREATE TABLE.
*
* @param mixed[] $column
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDateTypeDeclarationSQL(array $column)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Obtains DBMS specific SQL to be used to create time columns in statements
* like CREATE TABLE.
*
* @param mixed[] $column
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getTimeTypeDeclarationSQL(array $column)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* @param mixed[] $column
*
* @return string
*/
public function getFloatDeclarationSQL(array $column)
{
return 'DOUBLE PRECISION';
}
/**
* Gets the default transaction isolation level of the platform.
*
* @see TransactionIsolationLevel
*
* @return int The default isolation level.
*/
public function getDefaultTransactionIsolationLevel()
{
return TransactionIsolationLevel::READ_COMMITTED;
}
/* supports*() methods */
/**
* Whether the platform supports sequences.
*
* @return bool
*/
public function supportsSequences()
{
return false;
}
/**
* Whether the platform supports identity columns.
*
* Identity columns are columns that receive an auto-generated value from the
* database on insert of a row.
*
* @return bool
*/
public function supportsIdentityColumns()
{
return false;
}
/**
* Whether the platform emulates identity columns through sequences.
*
* Some platforms that do not support identity columns natively
* but support sequences can emulate identity columns by using
* sequences.
*
* @return bool
*/
public function usesSequenceEmulatedIdentityColumns()
{
return false;
}
/**
* Returns the name of the sequence for a particular identity column in a particular table.
*
* @see usesSequenceEmulatedIdentityColumns
*
* @param string $tableName The name of the table to return the sequence name for.
* @param string $columnName The name of the identity column in the table to return the sequence name for.
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getIdentitySequenceName($tableName, $columnName)
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Whether the platform supports indexes.
*
* @return bool
*/
public function supportsIndexes()
{
return true;
}
/**
* Whether the platform supports partial indexes.
*
* @return bool
*/
public function supportsPartialIndexes()
{
return false;
}
/**
* Whether the platform supports indexes with column length definitions.
*/
public function supportsColumnLengthIndexes(): bool
{
return false;
}
/**
* Whether the platform supports altering tables.
*
* @return bool
*/
public function supportsAlterTable()
{
return true;
}
/**
* Whether the platform supports transactions.
*
* @return bool
*/
public function supportsTransactions()
{
return true;
}
/**
* Whether the platform supports savepoints.
*
* @return bool
*/
public function supportsSavepoints()
{
return true;
}
/**
* Whether the platform supports releasing savepoints.
*
* @return bool
*/
public function supportsReleaseSavepoints()
{
return $this->supportsSavepoints();
}
/**
* Whether the platform supports primary key constraints.
*
* @return bool
*/
public function supportsPrimaryConstraints()
{
return true;
}
/**
* Whether the platform supports foreign key constraints.
*
* @return bool
*/
public function supportsForeignKeyConstraints()
{
return true;
}
/**
* Whether this platform supports onUpdate in foreign key constraints.
*
* @return bool
*/
public function supportsForeignKeyOnUpdate()
{
return $this->supportsForeignKeyConstraints();
}
/**
* Whether the platform supports database schemas.
*
* @return bool
*/
public function supportsSchemas()
{
return false;
}
/**
* Whether this platform can emulate schemas.
*
* Platforms that either support or emulate schemas don't automatically
* filter a schema for the namespaced elements in {@link AbstractManager::createSchema()}.
*
* @return bool
*/
public function canEmulateSchemas()
{
return false;
}
/**
* Returns the default schema name.
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
public function getDefaultSchemaName()
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Whether this platform supports create database.
*
* Some databases don't allow to create and drop databases at all or only with certain tools.
*
* @return bool
*/
public function supportsCreateDropDatabase()
{
return true;
}
/**
* Whether the platform supports getting the affected rows of a recent update/delete type query.
*
* @return bool
*/
public function supportsGettingAffectedRows()
{
return true;
}
/**
* Whether this platform support to add inline column comments as postfix.
*
* @return bool
*/
public function supportsInlineColumnComments()
{
return false;
}
/**
* Whether this platform support the proprietary syntax "COMMENT ON asset".
*
* @return bool
*/
public function supportsCommentOnStatement()
{
return false;
}
/**
* Does this platform have native guid type.
*
* @return bool
*/
public function hasNativeGuidType()
{
return false;
}
/**
* Does this platform have native JSON type.
*
* @return bool
*/
public function hasNativeJsonType()
{
return false;
}
/**
* @deprecated
*
* @return string
*
* @todo Remove in 3.0
*/
public function getIdentityColumnNullInsertSQL()
{
return '';
}
/**
* Whether this platform supports views.
*
* @return bool
*/
public function supportsViews()
{
return true;
}
/**
* Does this platform support column collation?
*
* @return bool
*/
public function supportsColumnCollation()
{
return false;
}
/**
* Gets the format string, as accepted by the date() function, that describes
* the format of a stored datetime value of this platform.
*
* @return string The format string.
*/
public function getDateTimeFormatString()
{
return 'Y-m-d H:i:s';
}
/**
* Gets the format string, as accepted by the date() function, that describes
* the format of a stored datetime with timezone value of this platform.
*
* @return string The format string.
*/
public function getDateTimeTzFormatString()
{
return 'Y-m-d H:i:s';
}
/**
* Gets the format string, as accepted by the date() function, that describes
* the format of a stored date value of this platform.
*
* @return string The format string.
*/
public function getDateFormatString()
{
return 'Y-m-d';
}
/**
* Gets the format string, as accepted by the date() function, that describes
* the format of a stored time value of this platform.
*
* @return string The format string.
*/
public function getTimeFormatString()
{
return 'H:i:s';
}
/**
* Adds an driver-specific LIMIT clause to the query.
*
* @param string $query
* @param int|null $limit
* @param int|null $offset
*
* @return string
*
* @throws DBALException
*/
final public function modifyLimitQuery($query, $limit, $offset = null)
{
if ($limit !== null) {
$limit = (int) $limit;
}
$offset = (int) $offset;
if ($offset < 0) {
throw new DBALException(sprintf(
'Offset must be a positive integer or zero, %d given',
$offset
));
}
if ($offset > 0 && ! $this->supportsLimitOffset()) {
throw new DBALException(sprintf(
'Platform %s does not support offset values in limit queries.',
$this->getName()
));
}
return $this->doModifyLimitQuery($query, $limit, $offset);
}
/**
* Adds an platform-specific LIMIT clause to the query.
*
* @param string $query
* @param int|null $limit
* @param int|null $offset
*
* @return string
*/
protected function doModifyLimitQuery($query, $limit, $offset)
{
if ($limit !== null) {
$query .= ' LIMIT ' . $limit;
}
if ($offset > 0) {
$query .= ' OFFSET ' . $offset;
}
return $query;
}
/**
* Whether the database platform support offsets in modify limit clauses.
*
* @return bool
*/
public function supportsLimitOffset()
{
return true;
}
/**
* Gets the character casing of a column in an SQL result set of this platform.
*
* @param string $column The column name for which to get the correct character casing.
*
* @return string The column name in the character casing used in SQL result sets.
*/
public function getSQLResultCasing($column)
{
return $column;
}
/**
* Makes any fixes to a name of a schema element (table, sequence, ...) that are required
* by restrictions of the platform, like a maximum length.
*
* @param string $schemaElementName
*
* @return string
*/
public function fixSchemaElementName($schemaElementName)
{
return $schemaElementName;
}
/**
* Maximum length of any given database identifier, like tables or column names.
*
* @return int
*/
public function getMaxIdentifierLength()
{
return 63;
}
/**
* Returns the insert SQL for an empty insert statement.
*
* @param string $quotedTableName
* @param string $quotedIdentifierColumnName
*
* @return string
*/
public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName)
{
return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (null)';
}
/**
* Generates a Truncate Table SQL statement for a given table.
*
* Cascade is not supported on many platforms but would optionally cascade the truncate by
* following the foreign keys.
*
* @param string $tableName
* @param bool $cascade
*
* @return string
*/
public function getTruncateTableSQL($tableName, $cascade = false)
{
$tableIdentifier = new Identifier($tableName);
return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
}
/**
* This is for test reasons, many vendors have special requirements for dummy statements.
*
* @return string
*/
public function getDummySelectSQL()
{
$expression = func_num_args() > 0 ? func_get_arg(0) : '1';
return sprintf('SELECT %s', $expression);
}
/**
* Returns the SQL to create a new savepoint.
*
* @param string $savepoint
*
* @return string
*/
public function createSavePoint($savepoint)
{
return 'SAVEPOINT ' . $savepoint;
}
/**
* Returns the SQL to release a savepoint.
*
* @param string $savepoint
*
* @return string
*/
public function releaseSavePoint($savepoint)
{
return 'RELEASE SAVEPOINT ' . $savepoint;
}
/**
* Returns the SQL to rollback a savepoint.
*
* @param string $savepoint
*
* @return string
*/
public function rollbackSavePoint($savepoint)
{
return 'ROLLBACK TO SAVEPOINT ' . $savepoint;
}
/**
* Returns the keyword list instance of this platform.
*
* @return KeywordList
*
* @throws DBALException If no keyword list is specified.
*/
final public function getReservedKeywordsList()
{
// Check for an existing instantiation of the keywords class.
if ($this->_keywords) {
return $this->_keywords;
}
$class = $this->getReservedKeywordsClass();
$keywords = new $class();
if (! $keywords instanceof KeywordList) {
throw DBALException::notSupported(__METHOD__);
}
// Store the instance so it doesn't need to be generated on every request.
$this->_keywords = $keywords;
return $keywords;
}
/**
* Returns the class name of the reserved keywords list.
*
* @return string
*
* @throws DBALException If not supported on this platform.
*/
protected function getReservedKeywordsClass()
{
throw DBALException::notSupported(__METHOD__);
}
/**
* Quotes a literal string.
* This method is NOT meant to fix SQL injections!
* It is only meant to escape this platform's string literal
* quote character inside the given literal string.
*
* @param string $str The literal string to be quoted.
*
* @return string The quoted literal string.
*/
public function quoteStringLiteral($str)
{
$c = $this->getStringLiteralQuoteCharacter();
return $c . str_replace($c, $c . $c, $str) . $c;
}
/**
* Gets the character used for string literal quoting.
*
* @return string
*/
public function getStringLiteralQuoteCharacter()
{
return "'";
}
/**
* Escapes metacharacters in a string intended to be used with a LIKE
* operator.
*
* @param string $inputString a literal, unquoted string
* @param string $escapeChar should be reused by the caller in the LIKE
* expression.
*/
final public function escapeStringForLike(string $inputString, string $escapeChar): string
{
return preg_replace(
'~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u',
addcslashes($escapeChar, '\\') . '$1',
$inputString
);
}
protected function getLikeWildcardCharacters(): string
{
return '%_';
}
}
lib/Doctrine/DBAL/Platforms/DB2Platform.php 0000644 00000061235 13727250467 0014343 0 ustar 00 getCharMaxLength();
}
return parent::getVarcharTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
public function getBlobTypeDeclarationSQL(array $column)
{
// todo blob(n) with $column['length'];
return 'BLOB(1M)';
}
/**
* {@inheritDoc}
*/
public function initializeDoctrineTypeMappings()
{
$this->doctrineTypeMapping = [
'smallint' => 'smallint',
'bigint' => 'bigint',
'integer' => 'integer',
'time' => 'time',
'date' => 'date',
'varchar' => 'string',
'character' => 'string',
'varbinary' => 'binary',
'binary' => 'binary',
'clob' => 'text',
'blob' => 'blob',
'decimal' => 'decimal',
'double' => 'float',
'real' => 'float',
'timestamp' => 'datetime',
];
}
/**
* {@inheritdoc}
*/
public function isCommentedDoctrineType(Type $doctrineType)
{
if ($doctrineType->getName() === Types::BOOLEAN) {
// We require a commented boolean type in order to distinguish between boolean and smallint
// as both (have to) map to the same native type.
return true;
}
return parent::isCommentedDoctrineType($doctrineType);
}
/**
* {@inheritDoc}
*/
protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
{
return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(254)')
: ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
}
/**
* {@inheritdoc}
*/
protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
{
return $this->getVarcharTypeDeclarationSQLSnippet($length, $fixed) . ' FOR BIT DATA';
}
/**
* {@inheritDoc}
*/
public function getClobTypeDeclarationSQL(array $column)
{
// todo clob(n) with $column['length'];
return 'CLOB(1M)';
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'db2';
}
/**
* {@inheritDoc}
*/
public function getBooleanTypeDeclarationSQL(array $column)
{
return 'SMALLINT';
}
/**
* {@inheritDoc}
*/
public function getIntegerTypeDeclarationSQL(array $column)
{
return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
public function getBigIntTypeDeclarationSQL(array $column)
{
return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
public function getSmallIntTypeDeclarationSQL(array $column)
{
return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
protected function _getCommonIntegerTypeDeclarationSQL(array $column)
{
$autoinc = '';
if (! empty($column['autoincrement'])) {
$autoinc = ' GENERATED BY DEFAULT AS IDENTITY';
}
return $autoinc;
}
/**
* {@inheritdoc}
*/
public function getBitAndComparisonExpression($value1, $value2)
{
return 'BITAND(' . $value1 . ', ' . $value2 . ')';
}
/**
* {@inheritdoc}
*/
public function getBitOrComparisonExpression($value1, $value2)
{
return 'BITOR(' . $value1 . ', ' . $value2 . ')';
}
/**
* {@inheritdoc}
*/
protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
{
switch ($unit) {
case DateIntervalUnit::WEEK:
$interval *= 7;
$unit = DateIntervalUnit::DAY;
break;
case DateIntervalUnit::QUARTER:
$interval *= 3;
$unit = DateIntervalUnit::MONTH;
break;
}
return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit;
}
/**
* {@inheritdoc}
*/
public function getDateDiffExpression($date1, $date2)
{
return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')';
}
/**
* {@inheritDoc}
*/
public function getDateTimeTypeDeclarationSQL(array $column)
{
if (isset($column['version']) && $column['version'] === true) {
return 'TIMESTAMP(0) WITH DEFAULT';
}
return 'TIMESTAMP(0)';
}
/**
* {@inheritDoc}
*/
public function getDateTypeDeclarationSQL(array $column)
{
return 'DATE';
}
/**
* {@inheritDoc}
*/
public function getTimeTypeDeclarationSQL(array $column)
{
return 'TIME';
}
/**
* {@inheritdoc}
*/
public function getTruncateTableSQL($tableName, $cascade = false)
{
$tableIdentifier = new Identifier($tableName);
return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this) . ' IMMEDIATE';
}
/**
* This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited.
*
* @param string $table
* @param string $database
*
* @return string
*/
public function getListTableColumnsSQL($table, $database = null)
{
$table = $this->quoteStringLiteral($table);
// We do the funky subquery and join syscat.columns.default this crazy way because
// as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB,
// it wants shorter stuff like a varchar.
return "
SELECT
cols.default,
subq.*
FROM (
SELECT DISTINCT
c.tabschema,
c.tabname,
c.colname,
c.colno,
c.typename,
c.nulls,
c.length,
c.scale,
c.identity,
tc.type AS tabconsttype,
c.remarks AS comment,
k.colseq,
CASE
WHEN c.generated = 'D' THEN 1
ELSE 0
END AS autoincrement
FROM syscat.columns c
LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
ON (k.tabschema = tc.tabschema
AND k.tabname = tc.tabname
AND tc.type = 'P'))
ON (c.tabschema = k.tabschema
AND c.tabname = k.tabname
AND c.colname = k.colname)
WHERE UPPER(c.tabname) = UPPER(" . $table . ')
ORDER BY c.colno
) subq
JOIN syscat.columns cols
ON subq.tabschema = cols.tabschema
AND subq.tabname = cols.tabname
AND subq.colno = cols.colno
ORDER BY subq.colno
';
}
/**
* {@inheritDoc}
*/
public function getListTablesSQL()
{
return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'";
}
/**
* {@inheritDoc}
*/
public function getListViewsSQL($database)
{
return 'SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS';
}
/**
* {@inheritDoc}
*/
public function getListTableIndexesSQL($table, $database = null)
{
$table = $this->quoteStringLiteral($table);
return "SELECT idx.INDNAME AS key_name,
idxcol.COLNAME AS column_name,
CASE
WHEN idx.UNIQUERULE = 'P' THEN 1
ELSE 0
END AS primary,
CASE
WHEN idx.UNIQUERULE = 'D' THEN 1
ELSE 0
END AS non_unique
FROM SYSCAT.INDEXES AS idx
JOIN SYSCAT.INDEXCOLUSE AS idxcol
ON idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME
WHERE idx.TABNAME = UPPER(" . $table . ')
ORDER BY idxcol.COLSEQ ASC';
}
/**
* {@inheritDoc}
*/
public function getListTableForeignKeysSQL($table)
{
$table = $this->quoteStringLiteral($table);
return "SELECT fkcol.COLNAME AS local_column,
fk.REFTABNAME AS foreign_table,
pkcol.COLNAME AS foreign_column,
fk.CONSTNAME AS index_name,
CASE
WHEN fk.UPDATERULE = 'R' THEN 'RESTRICT'
ELSE NULL
END AS on_update,
CASE
WHEN fk.DELETERULE = 'C' THEN 'CASCADE'
WHEN fk.DELETERULE = 'N' THEN 'SET NULL'
WHEN fk.DELETERULE = 'R' THEN 'RESTRICT'
ELSE NULL
END AS on_delete
FROM SYSCAT.REFERENCES AS fk
JOIN SYSCAT.KEYCOLUSE AS fkcol
ON fk.CONSTNAME = fkcol.CONSTNAME
AND fk.TABSCHEMA = fkcol.TABSCHEMA
AND fk.TABNAME = fkcol.TABNAME
JOIN SYSCAT.KEYCOLUSE AS pkcol
ON fk.REFKEYNAME = pkcol.CONSTNAME
AND fk.REFTABSCHEMA = pkcol.TABSCHEMA
AND fk.REFTABNAME = pkcol.TABNAME
WHERE fk.TABNAME = UPPER(" . $table . ')
ORDER BY fkcol.COLSEQ ASC';
}
/**
* {@inheritDoc}
*/
public function getCreateViewSQL($name, $sql)
{
return 'CREATE VIEW ' . $name . ' AS ' . $sql;
}
/**
* {@inheritDoc}
*/
public function getDropViewSQL($name)
{
return 'DROP VIEW ' . $name;
}
/**
* {@inheritDoc}
*/
public function getCreateDatabaseSQL($database)
{
return 'CREATE DATABASE ' . $database;
}
/**
* {@inheritDoc}
*/
public function getDropDatabaseSQL($database)
{
return 'DROP DATABASE ' . $database;
}
/**
* {@inheritDoc}
*/
public function supportsCreateDropDatabase()
{
return false;
}
/**
* {@inheritDoc}
*/
public function supportsReleaseSavepoints()
{
return false;
}
/**
* {@inheritdoc}
*/
public function supportsCommentOnStatement()
{
return true;
}
/**
* {@inheritDoc}
*/
public function getCurrentDateSQL()
{
return 'CURRENT DATE';
}
/**
* {@inheritDoc}
*/
public function getCurrentTimeSQL()
{
return 'CURRENT TIME';
}
/**
* {@inheritDoc}
*/
public function getCurrentTimestampSQL()
{
return 'CURRENT TIMESTAMP';
}
/**
* {@inheritDoc}
*/
public function getIndexDeclarationSQL($name, Index $index)
{
// Index declaration in statements like CREATE TABLE is not supported.
throw DBALException::notSupported(__METHOD__);
}
/**
* {@inheritDoc}
*/
protected function _getCreateTableSQL($name, array $columns, array $options = [])
{
$indexes = [];
if (isset($options['indexes'])) {
$indexes = $options['indexes'];
}
$options['indexes'] = [];
$sqls = parent::_getCreateTableSQL($name, $columns, $options);
foreach ($indexes as $definition) {
$sqls[] = $this->getCreateIndexSQL($definition, $name);
}
return $sqls;
}
/**
* {@inheritDoc}
*/
public function getAlterTableSQL(TableDiff $diff)
{
$sql = [];
$columnSql = [];
$commentsSQL = [];
$queryParts = [];
foreach ($diff->addedColumns as $column) {
if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
continue;
}
$columnDef = $column->toArray();
$queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef);
// Adding non-nullable columns to a table requires a default value to be specified.
if (
! empty($columnDef['notnull']) &&
! isset($columnDef['default']) &&
empty($columnDef['autoincrement'])
) {
$queryPart .= ' WITH DEFAULT';
}
$queryParts[] = $queryPart;
$comment = $this->getColumnComment($column);
if ($comment === null || $comment === '') {
continue;
}
$commentsSQL[] = $this->getCommentOnColumnSQL(
$diff->getName($this)->getQuotedName($this),
$column->getQuotedName($this),
$comment
);
}
foreach ($diff->removedColumns as $column) {
if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
continue;
}
$queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this);
}
foreach ($diff->changedColumns as $columnDiff) {
if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
continue;
}
if ($columnDiff->hasChanged('comment')) {
$commentsSQL[] = $this->getCommentOnColumnSQL(
$diff->getName($this)->getQuotedName($this),
$columnDiff->column->getQuotedName($this),
$this->getColumnComment($columnDiff->column)
);
if (count($columnDiff->changedProperties) === 1) {
continue;
}
}
$this->gatherAlterColumnSQL($diff->getName($this), $columnDiff, $sql, $queryParts);
}
foreach ($diff->renamedColumns as $oldColumnName => $column) {
if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
continue;
}
$oldColumnName = new Identifier($oldColumnName);
$queryParts[] = 'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .
' TO ' . $column->getQuotedName($this);
}
$tableSql = [];
if (! $this->onSchemaAlterTable($diff, $tableSql)) {
if (count($queryParts) > 0) {
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(' ', $queryParts);
}
// Some table alteration operations require a table reorganization.
if (! empty($diff->removedColumns) || ! empty($diff->changedColumns)) {
$sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')";
}
$sql = array_merge($sql, $commentsSQL);
$newName = $diff->getNewName();
if ($newName !== false) {
$sql[] = sprintf(
'RENAME TABLE %s TO %s',
$diff->getName($this)->getQuotedName($this),
$newName->getQuotedName($this)
);
}
$sql = array_merge(
$this->getPreAlterTableIndexForeignKeySQL($diff),
$sql,
$this->getPostAlterTableIndexForeignKeySQL($diff)
);
}
return array_merge($sql, $tableSql, $columnSql);
}
/**
* Gathers the table alteration SQL for a given column diff.
*
* @param Identifier $table The table to gather the SQL for.
* @param ColumnDiff $columnDiff The column diff to evaluate.
* @param string[] $sql The sequence of table alteration statements to fill.
* @param mixed[] $queryParts The sequence of column alteration clauses to fill.
*/
private function gatherAlterColumnSQL(
Identifier $table,
ColumnDiff $columnDiff,
array &$sql,
array &$queryParts
): void {
$alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff);
if (empty($alterColumnClauses)) {
return;
}
// If we have a single column alteration, we can append the clause to the main query.
if (count($alterColumnClauses) === 1) {
$queryParts[] = current($alterColumnClauses);
return;
}
// We have multiple alterations for the same column,
// so we need to trigger a complete ALTER TABLE statement
// for each ALTER COLUMN clause.
foreach ($alterColumnClauses as $alterColumnClause) {
$sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause;
}
}
/**
* Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff.
*
* @param ColumnDiff $columnDiff The column diff to evaluate.
*
* @return string[]
*/
private function getAlterColumnClausesSQL(ColumnDiff $columnDiff)
{
$column = $columnDiff->column->toArray();
$alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this);
if ($column['columnDefinition']) {
return [$alterClause . ' ' . $column['columnDefinition']];
}
$clauses = [];
if (
$columnDiff->hasChanged('type') ||
$columnDiff->hasChanged('length') ||
$columnDiff->hasChanged('precision') ||
$columnDiff->hasChanged('scale') ||
$columnDiff->hasChanged('fixed')
) {
$clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this);
}
if ($columnDiff->hasChanged('notnull')) {
$clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL';
}
if ($columnDiff->hasChanged('default')) {
if (isset($column['default'])) {
$defaultClause = $this->getDefaultValueDeclarationSQL($column);
if ($defaultClause) {
$clauses[] = $alterClause . ' SET' . $defaultClause;
}
} else {
$clauses[] = $alterClause . ' DROP DEFAULT';
}
}
return $clauses;
}
/**
* {@inheritDoc}
*/
protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
{
$sql = [];
$table = $diff->getName($this)->getQuotedName($this);
foreach ($diff->removedIndexes as $remKey => $remIndex) {
foreach ($diff->addedIndexes as $addKey => $addIndex) {
if ($remIndex->getColumns() !== $addIndex->getColumns()) {
continue;
}
if ($remIndex->isPrimary()) {
$sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
} elseif ($remIndex->isUnique()) {
$sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this);
} else {
$sql[] = $this->getDropIndexSQL($remIndex, $table);
}
$sql[] = $this->getCreateIndexSQL($addIndex, $table);
unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
break;
}
}
$sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
return $sql;
}
/**
* {@inheritdoc}
*/
protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
{
if (strpos($tableName, '.') !== false) {
[$schema] = explode('.', $tableName);
$oldIndexName = $schema . '.' . $oldIndexName;
}
return ['RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)];
}
/**
* {@inheritDoc}
*/
public function getDefaultValueDeclarationSQL($column)
{
if (! empty($column['autoincrement'])) {
return '';
}
if (isset($column['version']) && $column['version']) {
if ((string) $column['type'] !== 'DateTime') {
$column['default'] = '1';
}
}
return parent::getDefaultValueDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName)
{
return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)';
}
/**
* {@inheritDoc}
*/
public function getCreateTemporaryTableSnippetSQL()
{
return 'DECLARE GLOBAL TEMPORARY TABLE';
}
/**
* {@inheritDoc}
*/
public function getTemporaryTableName($tableName)
{
return 'SESSION.' . $tableName;
}
/**
* {@inheritDoc}
*/
protected function doModifyLimitQuery($query, $limit, $offset = null)
{
$where = [];
if ($offset > 0) {
$where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1);
}
if ($limit !== null) {
$where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit);
}
if (empty($where)) {
return $query;
}
// Todo OVER() needs ORDER BY data!
return sprintf(
'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s',
$query,
implode(' AND ', $where)
);
}
/**
* {@inheritDoc}
*/
public function getLocateExpression($str, $substr, $startPos = false)
{
if ($startPos === false) {
return 'LOCATE(' . $substr . ', ' . $str . ')';
}
return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')';
}
/**
* {@inheritDoc}
*/
public function getSubstringExpression($string, $start, $length = null)
{
if ($length === null) {
return 'SUBSTR(' . $string . ', ' . $start . ')';
}
return 'SUBSTR(' . $string . ', ' . $start . ', ' . $length . ')';
}
/**
* {@inheritDoc}
*/
public function supportsIdentityColumns()
{
return true;
}
/**
* {@inheritDoc}
*/
public function prefersIdentityColumns()
{
return true;
}
/**
* {@inheritDoc}
*
* DB2 returns all column names in SQL result sets in uppercase.
*/
public function getSQLResultCasing($column)
{
return strtoupper($column);
}
/**
* {@inheritDoc}
*/
public function getForUpdateSQL()
{
return ' WITH RR USE AND KEEP UPDATE LOCKS';
}
/**
* {@inheritDoc}
*/
public function getDummySelectSQL()
{
$expression = func_num_args() > 0 ? func_get_arg(0) : '1';
return sprintf('SELECT %s FROM sysibm.sysdummy1', $expression);
}
/**
* {@inheritDoc}
*
* DB2 supports savepoints, but they work semantically different than on other vendor platforms.
*
* TODO: We have to investigate how to get DB2 up and running with savepoints.
*/
public function supportsSavepoints()
{
return false;
}
/**
* {@inheritDoc}
*/
protected function getReservedKeywordsClass()
{
return Keywords\DB2Keywords::class;
}
public function getListTableCommentsSQL(string $table): string
{
return sprintf(
<<<'SQL'
SELECT REMARKS
FROM SYSIBM.SYSTABLES
WHERE NAME = UPPER( %s )
SQL
,
$this->quoteStringLiteral($table)
);
}
}
lib/Doctrine/DBAL/Platforms/DateIntervalUnit.php 0000644 00000000726 13727250467 0015507 0 ustar 00 _getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
protected function _getCommonIntegerTypeDeclarationSQL(array $column)
{
$autoinc = '';
if (! empty($column['autoincrement'])) {
$autoinc = ' AUTO_INCREMENT';
}
return $autoinc;
}
/**
* {@inheritDoc}
*/
public function getBigIntTypeDeclarationSQL(array $column)
{
return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
public function getSmallIntTypeDeclarationSQL(array $column)
{
return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
{
return $length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)';
}
/**
* {@inheritdoc}
*/
protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
{
return 'VARBINARY(' . ($length ?: 255) . ')';
}
/**
* {@inheritDoc}
*/
protected function initializeDoctrineTypeMappings()
{
$this->doctrineTypeMapping = [
'boolean' => 'boolean',
'varchar' => 'string',
'varbinary' => 'binary',
'integer' => 'integer',
'blob' => 'blob',
'decimal' => 'decimal',
'datetime' => 'datetime',
'date' => 'date',
'time' => 'time',
'text' => 'text',
'timestamp' => 'datetime',
'double' => 'float',
'bigint' => 'bigint',
];
}
/**
* {@inheritDoc}
*/
public function getClobTypeDeclarationSQL(array $column)
{
return 'TEXT';
}
/**
* {@inheritDoc}
*/
public function getBlobTypeDeclarationSQL(array $column)
{
return 'BLOB';
}
/**
* {@inheritDoc}
*/
public function getCreateDatabaseSQL($name)
{
return 'CREATE DATABASE ' . $name;
}
/**
* {@inheritDoc}
*/
public function getDropDatabaseSQL($name)
{
return 'DROP DATABASE ' . $name;
}
/**
* {@inheritDoc}
*/
protected function _getCreateTableSQL($name, array $columns, array $options = [])
{
$queryFields = $this->getColumnDeclarationListSQL($columns);
if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
foreach ($options['uniqueConstraints'] as $index => $definition) {
$queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition);
}
}
// add all indexes
if (isset($options['indexes']) && ! empty($options['indexes'])) {
foreach ($options['indexes'] as $index => $definition) {
$queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
}
}
// attach all primary keys
if (isset($options['primary']) && ! empty($options['primary'])) {
$keyColumns = array_unique(array_values($options['primary']));
$queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
}
$query = 'CREATE ';
if (! empty($options['temporary'])) {
$query .= 'TEMPORARY ';
}
$query .= 'TABLE ' . $name . ' (' . $queryFields . ') ';
$query .= $this->buildTableOptions($options);
$query .= $this->buildPartitionOptions($options);
$sql = [$query];
if (isset($options['foreignKeys'])) {
foreach ((array) $options['foreignKeys'] as $definition) {
$sql[] = $this->getCreateForeignKeySQL($definition, $name);
}
}
return $sql;
}
/**
* Build SQL for table options
*
* @param mixed[] $options
*
* @return string
*/
private function buildTableOptions(array $options)
{
if (isset($options['table_options'])) {
return $options['table_options'];
}
$tableOptions = [];
// Collate
if (! isset($options['collate'])) {
$options['collate'] = 'utf8_unicode_ci';
}
$tableOptions[] = sprintf('COLLATE %s', $options['collate']);
// Engine
if (! isset($options['engine'])) {
$options['engine'] = 'InnoDB';
}
$tableOptions[] = sprintf('ENGINE = %s', $options['engine']);
// Auto increment
if (isset($options['auto_increment'])) {
$tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']);
}
// Comment
if (isset($options['comment'])) {
$comment = trim($options['comment'], " '");
$tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($comment));
}
// Row format
if (isset($options['row_format'])) {
$tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']);
}
return implode(' ', $tableOptions);
}
/**
* Build SQL for partition options.
*
* @param mixed[] $options
*
* @return string
*/
private function buildPartitionOptions(array $options)
{
return isset($options['partition_options'])
? ' ' . $options['partition_options']
: '';
}
/**
* {@inheritDoc}
*/
public function getListDatabasesSQL()
{
return "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE CATALOG_NAME='LOCAL'";
}
/**
* {@inheritDoc}
*/
protected function getReservedKeywordsClass()
{
return Keywords\DrizzleKeywords::class;
}
/**
* {@inheritDoc}
*/
public function getListTablesSQL()
{
return "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE' AND TABLE_SCHEMA=DATABASE()";
}
/**
* {@inheritDoc}
*/
public function getListTableColumnsSQL($table, $database = null)
{
if ($database) {
$databaseSQL = $this->quoteStringLiteral($database);
} else {
$databaseSQL = 'DATABASE()';
}
return 'SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE, IS_AUTO_INCREMENT,' .
' CHARACTER_MAXIMUM_LENGTH, COLUMN_DEFAULT, NUMERIC_PRECISION, NUMERIC_SCALE, COLLATION_NAME' .
' FROM DATA_DICTIONARY.COLUMNS' .
' WHERE TABLE_SCHEMA=' . $databaseSQL . ' AND TABLE_NAME = ' . $this->quoteStringLiteral($table);
}
/**
* @param string $table
* @param string|null $database
*
* @return string
*/
public function getListTableForeignKeysSQL($table, $database = null)
{
if ($database) {
$databaseSQL = $this->quoteStringLiteral($database);
} else {
$databaseSQL = 'DATABASE()';
}
return 'SELECT CONSTRAINT_NAME, CONSTRAINT_COLUMNS, REFERENCED_TABLE_NAME, REFERENCED_TABLE_COLUMNS,'
. ' UPDATE_RULE, DELETE_RULE'
. ' FROM DATA_DICTIONARY.FOREIGN_KEYS'
. ' WHERE CONSTRAINT_SCHEMA=' . $databaseSQL
. ' AND CONSTRAINT_TABLE=' . $this->quoteStringLiteral($table);
}
/**
* {@inheritDoc}
*/
public function getListTableIndexesSQL($table, $database = null)
{
if ($database) {
$databaseSQL = $this->quoteStringLiteral($database);
} else {
$databaseSQL = 'DATABASE()';
}
return "SELECT INDEX_NAME AS 'key_name',"
. " COLUMN_NAME AS 'column_name',"
. " IS_USED_IN_PRIMARY AS 'primary',"
. " IS_UNIQUE=0 AS 'non_unique'"
. ' FROM DATA_DICTIONARY.INDEX_PARTS'
. ' WHERE TABLE_SCHEMA=' . $databaseSQL . ' AND TABLE_NAME=' . $this->quoteStringLiteral($table);
}
/**
* {@inheritDoc}
*/
public function prefersIdentityColumns()
{
return true;
}
/**
* {@inheritDoc}
*/
public function supportsIdentityColumns()
{
return true;
}
/**
* {@inheritDoc}
*/
public function supportsInlineColumnComments()
{
return true;
}
/**
* {@inheritDoc}
*/
public function supportsViews()
{
return false;
}
/**
* {@inheritdoc}
*/
public function supportsColumnCollation()
{
return true;
}
/**
* {@inheritDoc}
*/
public function getDropIndexSQL($index, $table = null)
{
if ($index instanceof Index) {
$indexName = $index->getQuotedName($this);
} elseif (is_string($index)) {
$indexName = $index;
} else {
throw new InvalidArgumentException(
__METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.'
);
}
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
} elseif (! is_string($table)) {
throw new InvalidArgumentException(
__METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.'
);
}
if ($index instanceof Index && $index->isPrimary()) {
// drizzle primary keys are always named "PRIMARY",
// so we cannot use them in statements because of them being keyword.
return $this->getDropPrimaryKeySQL($table);
}
return 'DROP INDEX ' . $indexName . ' ON ' . $table;
}
/**
* @param string $table
*
* @return string
*/
protected function getDropPrimaryKeySQL($table)
{
return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
}
/**
* {@inheritDoc}
*/
public function getDateTimeTypeDeclarationSQL(array $column)
{
if (isset($column['version']) && $column['version'] === true) {
return 'TIMESTAMP';
}
return 'DATETIME';
}
/**
* {@inheritDoc}
*/
public function getTimeTypeDeclarationSQL(array $column)
{
return 'TIME';
}
/**
* {@inheritDoc}
*/
public function getDateTypeDeclarationSQL(array $column)
{
return 'DATE';
}
/**
* {@inheritDoc}
*/
public function getAlterTableSQL(TableDiff $diff)
{
$columnSql = [];
$queryParts = [];
$newName = $diff->getNewName();
if ($newName !== false) {
$queryParts[] = 'RENAME TO ' . $newName->getQuotedName($this);
}
foreach ($diff->addedColumns as $column) {
if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
continue;
}
$columnArray = array_merge($column->toArray(), [
'comment' => $this->getColumnComment($column),
]);
$queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
}
foreach ($diff->removedColumns as $column) {
if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
continue;
}
$queryParts[] = 'DROP ' . $column->getQuotedName($this);
}
foreach ($diff->changedColumns as $columnDiff) {
if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
continue;
}
$column = $columnDiff->column;
$columnArray = $column->toArray();
// Do not generate column alteration clause if type is binary and only fixed property has changed.
// Drizzle only supports binary type columns with variable length.
// Avoids unnecessary table alteration statements.
if (
$columnArray['type'] instanceof BinaryType &&
$columnDiff->hasChanged('fixed') &&
count($columnDiff->changedProperties) === 1
) {
continue;
}
$columnArray['comment'] = $this->getColumnComment($column);
$queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' '
. $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
}
foreach ($diff->renamedColumns as $oldColumnName => $column) {
if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
continue;
}
$oldColumnName = new Identifier($oldColumnName);
$columnArray = $column->toArray();
$columnArray['comment'] = $this->getColumnComment($column);
$queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' '
. $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
}
$sql = [];
$tableSql = [];
if (! $this->onSchemaAlterTable($diff, $tableSql)) {
if (count($queryParts) > 0) {
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this)
. ' ' . implode(', ', $queryParts);
}
$sql = array_merge(
$this->getPreAlterTableIndexForeignKeySQL($diff),
$sql,
$this->getPostAlterTableIndexForeignKeySQL($diff)
);
}
return array_merge($sql, $tableSql, $columnSql);
}
/**
* {@inheritDoc}
*/
public function getDropTemporaryTableSQL($table)
{
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
} elseif (! is_string($table)) {
throw new InvalidArgumentException(
__METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.'
);
}
return 'DROP TEMPORARY TABLE ' . $table;
}
/**
* {@inheritDoc}
*/
public function convertBooleans($item)
{
if (is_array($item)) {
foreach ($item as $key => $value) {
if (! is_bool($value) && ! is_numeric($value)) {
continue;
}
$item[$key] = $value ? 'true' : 'false';
}
} elseif (is_bool($item) || is_numeric($item)) {
$item = $item ? 'true' : 'false';
}
return $item;
}
/**
* {@inheritDoc}
*/
public function getLocateExpression($str, $substr, $startPos = false)
{
if ($startPos === false) {
return 'LOCATE(' . $substr . ', ' . $str . ')';
}
return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')';
}
/**
* {@inheritDoc}
*
* @deprecated Use application-generated UUIDs instead
*/
public function getGuidExpression()
{
return 'UUID()';
}
/**
* {@inheritDoc}
*/
public function getRegexpExpression()
{
return 'RLIKE';
}
}
lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php 0000644 00000022220 13727250467 0016164 0 ustar 00 keywords === null) {
$this->initializeKeywords();
}
return isset($this->keywords[strtoupper($word)]);
}
/**
* @return void
*/
protected function initializeKeywords()
{
$this->keywords = array_flip(array_map('strtoupper', $this->getKeywords()));
}
/**
* Returns the list of keywords.
*
* @return string[]
*/
abstract protected function getKeywords();
/**
* Returns the name of this keyword list.
*
* @return string
*/
abstract public function getName();
}
lib/Doctrine/DBAL/Platforms/Keywords/MariaDb102Keywords.php 0000644 00000013731 13727250467 0017346 0 ustar 00 keywordLists = $keywordLists;
}
/**
* @return string[]
*/
public function getViolations()
{
return $this->violations;
}
/**
* @param string $word
*
* @return string[]
*/
private function isReservedWord($word)
{
if ($word[0] === '`') {
$word = str_replace('`', '', $word);
}
$keywordLists = [];
foreach ($this->keywordLists as $keywordList) {
if (! $keywordList->isKeyword($word)) {
continue;
}
$keywordLists[] = $keywordList->getName();
}
return $keywordLists;
}
/**
* @param string $asset
* @param string[] $violatedPlatforms
*
* @return void
*/
private function addViolation($asset, $violatedPlatforms)
{
if (! $violatedPlatforms) {
return;
}
$this->violations[] = $asset . ' keyword violations: ' . implode(', ', $violatedPlatforms);
}
/**
* {@inheritdoc}
*/
public function acceptColumn(Table $table, Column $column)
{
$this->addViolation(
'Table ' . $table->getName() . ' column ' . $column->getName(),
$this->isReservedWord($column->getName())
);
}
/**
* {@inheritdoc}
*/
public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint)
{
}
/**
* {@inheritdoc}
*/
public function acceptIndex(Table $table, Index $index)
{
}
/**
* {@inheritdoc}
*/
public function acceptSchema(Schema $schema)
{
}
/**
* {@inheritdoc}
*/
public function acceptSequence(Sequence $sequence)
{
}
/**
* {@inheritdoc}
*/
public function acceptTable(Table $table)
{
$this->addViolation(
'Table ' . $table->getName(),
$this->isReservedWord($table->getName())
);
}
}
lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere11Keywords.php 0000644 00000001334 13727250467 0020124 0 ustar 00 doctrineTypeMapping['json'] = Types::JSON;
}
}
lib/Doctrine/DBAL/Platforms/MySQL57Platform.php 0000644 00000002662 13727250467 0015114 0 ustar 00 getQuotedName($this)];
}
/**
* {@inheritdoc}
*/
protected function getReservedKeywordsClass()
{
return Keywords\MySQL57Keywords::class;
}
/**
* {@inheritdoc}
*/
protected function initializeDoctrineTypeMappings()
{
parent::initializeDoctrineTypeMappings();
$this->doctrineTypeMapping['json'] = Types::JSON;
}
}
lib/Doctrine/DBAL/Platforms/MySQL80Platform.php 0000644 00000000530 13727250467 0015100 0 ustar 00 0) {
$query .= ' OFFSET ' . $offset;
}
} elseif ($offset > 0) {
// 2^64-1 is the maximum of unsigned BIGINT, the biggest limit possible
$query .= ' LIMIT 18446744073709551615 OFFSET ' . $offset;
}
return $query;
}
/**
* {@inheritDoc}
*/
public function getIdentifierQuoteCharacter()
{
return '`';
}
/**
* {@inheritDoc}
*/
public function getRegexpExpression()
{
return 'RLIKE';
}
/**
* {@inheritDoc}
*
* @deprecated Use application-generated UUIDs instead
*/
public function getGuidExpression()
{
return 'UUID()';
}
/**
* {@inheritDoc}
*/
public function getLocateExpression($str, $substr, $startPos = false)
{
if ($startPos === false) {
return 'LOCATE(' . $substr . ', ' . $str . ')';
}
return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')';
}
/**
* {@inheritDoc}
*/
public function getConcatExpression()
{
return sprintf('CONCAT(%s)', implode(', ', func_get_args()));
}
/**
* {@inheritdoc}
*/
protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
{
$function = $operator === '+' ? 'DATE_ADD' : 'DATE_SUB';
return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')';
}
/**
* {@inheritDoc}
*/
public function getDateDiffExpression($date1, $date2)
{
return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')';
}
/**
* {@inheritDoc}
*/
public function getListDatabasesSQL()
{
return 'SHOW DATABASES';
}
/**
* {@inheritDoc}
*/
public function getListTableConstraintsSQL($table)
{
return 'SHOW INDEX FROM ' . $table;
}
/**
* {@inheritDoc}
*
* Two approaches to listing the table indexes. The information_schema is
* preferred, because it doesn't cause problems with SQL keywords such as "order" or "table".
*/
public function getListTableIndexesSQL($table, $database = null)
{
if ($database) {
$database = $this->quoteStringLiteral($database);
$table = $this->quoteStringLiteral($table);
return 'SELECT NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, COLUMN_NAME AS Column_Name,' .
' SUB_PART AS Sub_Part, INDEX_TYPE AS Index_Type' .
' FROM information_schema.STATISTICS WHERE TABLE_NAME = ' . $table .
' AND TABLE_SCHEMA = ' . $database .
' ORDER BY SEQ_IN_INDEX ASC';
}
return 'SHOW INDEX FROM ' . $table;
}
/**
* {@inheritDoc}
*/
public function getListViewsSQL($database)
{
$database = $this->quoteStringLiteral($database);
return 'SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = ' . $database;
}
/**
* @param string $table
* @param string|null $database
*
* @return string
*/
public function getListTableForeignKeysSQL($table, $database = null)
{
$table = $this->quoteStringLiteral($table);
if ($database !== null) {
$database = $this->quoteStringLiteral($database);
}
$sql = 'SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, ' .
'k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ ' .
'FROM information_schema.key_column_usage k /*!50116 ' .
'INNER JOIN information_schema.referential_constraints c ON ' .
' c.constraint_name = k.constraint_name AND ' .
' c.table_name = ' . $table . ' */ WHERE k.table_name = ' . $table;
$databaseNameSql = $database ?? 'DATABASE()';
return $sql . ' AND k.table_schema = ' . $databaseNameSql
. ' /*!50116 AND c.constraint_schema = ' . $databaseNameSql . ' */'
. ' AND k.`REFERENCED_COLUMN_NAME` is not NULL';
}
/**
* {@inheritDoc}
*/
public function getCreateViewSQL($name, $sql)
{
return 'CREATE VIEW ' . $name . ' AS ' . $sql;
}
/**
* {@inheritDoc}
*/
public function getDropViewSQL($name)
{
return 'DROP VIEW ' . $name;
}
/**
* {@inheritDoc}
*/
protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
{
return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
: ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
}
/**
* {@inheritdoc}
*/
protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
{
return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')';
}
/**
* Gets the SQL snippet used to declare a CLOB column type.
* TINYTEXT : 2 ^ 8 - 1 = 255
* TEXT : 2 ^ 16 - 1 = 65535
* MEDIUMTEXT : 2 ^ 24 - 1 = 16777215
* LONGTEXT : 2 ^ 32 - 1 = 4294967295
*
* {@inheritDoc}
*/
public function getClobTypeDeclarationSQL(array $column)
{
if (! empty($column['length']) && is_numeric($column['length'])) {
$length = $column['length'];
if ($length <= static::LENGTH_LIMIT_TINYTEXT) {
return 'TINYTEXT';
}
if ($length <= static::LENGTH_LIMIT_TEXT) {
return 'TEXT';
}
if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) {
return 'MEDIUMTEXT';
}
}
return 'LONGTEXT';
}
/**
* {@inheritDoc}
*/
public function getDateTimeTypeDeclarationSQL(array $column)
{
if (isset($column['version']) && $column['version'] === true) {
return 'TIMESTAMP';
}
return 'DATETIME';
}
/**
* {@inheritDoc}
*/
public function getDateTypeDeclarationSQL(array $column)
{
return 'DATE';
}
/**
* {@inheritDoc}
*/
public function getTimeTypeDeclarationSQL(array $column)
{
return 'TIME';
}
/**
* {@inheritDoc}
*/
public function getBooleanTypeDeclarationSQL(array $column)
{
return 'TINYINT(1)';
}
/**
* Obtain DBMS specific SQL code portion needed to set the COLLATION
* of a column declaration to be used in statements like CREATE TABLE.
*
* @deprecated Deprecated since version 2.5, Use {@link self::getColumnCollationDeclarationSQL()} instead.
*
* @param string $collation name of the collation
*
* @return string DBMS specific SQL code portion needed to set the COLLATION
* of a column declaration.
*/
public function getCollationFieldDeclaration($collation)
{
return $this->getColumnCollationDeclarationSQL($collation);
}
/**
* {@inheritDoc}
*
* MySql prefers "autoincrement" identity columns since sequences can only
* be emulated with a table.
*/
public function prefersIdentityColumns()
{
return true;
}
/**
* {@inheritDoc}
*
* MySql supports this through AUTO_INCREMENT columns.
*/
public function supportsIdentityColumns()
{
return true;
}
/**
* {@inheritDoc}
*/
public function supportsInlineColumnComments()
{
return true;
}
/**
* {@inheritDoc}
*/
public function supportsColumnCollation()
{
return true;
}
/**
* {@inheritDoc}
*/
public function getListTablesSQL()
{
return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'";
}
/**
* {@inheritDoc}
*/
public function getListTableColumnsSQL($table, $database = null)
{
$table = $this->quoteStringLiteral($table);
if ($database) {
$database = $this->quoteStringLiteral($database);
} else {
$database = 'DATABASE()';
}
return 'SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ' .
'COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, ' .
'CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation ' .
'FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ' . $database . ' AND TABLE_NAME = ' . $table .
' ORDER BY ORDINAL_POSITION ASC';
}
public function getListTableMetadataSQL(string $table, ?string $database = null): string
{
return sprintf(
<<<'SQL'
SELECT ENGINE, AUTO_INCREMENT, TABLE_COLLATION, TABLE_COMMENT, CREATE_OPTIONS
FROM information_schema.TABLES
WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = %s AND TABLE_NAME = %s
SQL
,
$database ? $this->quoteStringLiteral($database) : 'DATABASE()',
$this->quoteStringLiteral($table)
);
}
/**
* {@inheritDoc}
*/
public function getCreateDatabaseSQL($name)
{
return 'CREATE DATABASE ' . $name;
}
/**
* {@inheritDoc}
*/
public function getDropDatabaseSQL($name)
{
return 'DROP DATABASE ' . $name;
}
/**
* {@inheritDoc}
*/
protected function _getCreateTableSQL($name, array $columns, array $options = [])
{
$queryFields = $this->getColumnDeclarationListSQL($columns);
if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
foreach ($options['uniqueConstraints'] as $index => $definition) {
$queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition);
}
}
// add all indexes
if (isset($options['indexes']) && ! empty($options['indexes'])) {
foreach ($options['indexes'] as $index => $definition) {
$queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition);
}
}
// attach all primary keys
if (isset($options['primary']) && ! empty($options['primary'])) {
$keyColumns = array_unique(array_values($options['primary']));
$queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
}
$query = 'CREATE ';
if (! empty($options['temporary'])) {
$query .= 'TEMPORARY ';
}
$query .= 'TABLE ' . $name . ' (' . $queryFields . ') ';
$query .= $this->buildTableOptions($options);
$query .= $this->buildPartitionOptions($options);
$sql = [$query];
$engine = 'INNODB';
if (isset($options['engine'])) {
$engine = strtoupper(trim($options['engine']));
}
// Propagate foreign key constraints only for InnoDB.
if (isset($options['foreignKeys']) && $engine === 'INNODB') {
foreach ((array) $options['foreignKeys'] as $definition) {
$sql[] = $this->getCreateForeignKeySQL($definition, $name);
}
}
return $sql;
}
/**
* {@inheritdoc}
*/
public function getDefaultValueDeclarationSQL($column)
{
// Unset the default value if the given column definition does not allow default values.
if ($column['type'] instanceof TextType || $column['type'] instanceof BlobType) {
$column['default'] = null;
}
return parent::getDefaultValueDeclarationSQL($column);
}
/**
* Build SQL for table options
*
* @param mixed[] $options
*
* @return string
*/
private function buildTableOptions(array $options)
{
if (isset($options['table_options'])) {
return $options['table_options'];
}
$tableOptions = [];
// Charset
if (! isset($options['charset'])) {
$options['charset'] = 'utf8';
}
$tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']);
// Collate
if (! isset($options['collate'])) {
$options['collate'] = $options['charset'] . '_unicode_ci';
}
$tableOptions[] = $this->getColumnCollationDeclarationSQL($options['collate']);
// Engine
if (! isset($options['engine'])) {
$options['engine'] = 'InnoDB';
}
$tableOptions[] = sprintf('ENGINE = %s', $options['engine']);
// Auto increment
if (isset($options['auto_increment'])) {
$tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']);
}
// Comment
if (isset($options['comment'])) {
$tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($options['comment']));
}
// Row format
if (isset($options['row_format'])) {
$tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']);
}
return implode(' ', $tableOptions);
}
/**
* Build SQL for partition options.
*
* @param mixed[] $options
*
* @return string
*/
private function buildPartitionOptions(array $options)
{
return isset($options['partition_options'])
? ' ' . $options['partition_options']
: '';
}
/**
* {@inheritDoc}
*/
public function getAlterTableSQL(TableDiff $diff)
{
$columnSql = [];
$queryParts = [];
$newName = $diff->getNewName();
if ($newName !== false) {
$queryParts[] = 'RENAME TO ' . $newName->getQuotedName($this);
}
foreach ($diff->addedColumns as $column) {
if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
continue;
}
$columnArray = array_merge($column->toArray(), [
'comment' => $this->getColumnComment($column),
]);
$queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
}
foreach ($diff->removedColumns as $column) {
if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
continue;
}
$queryParts[] = 'DROP ' . $column->getQuotedName($this);
}
foreach ($diff->changedColumns as $columnDiff) {
if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
continue;
}
$column = $columnDiff->column;
$columnArray = $column->toArray();
// Don't propagate default value changes for unsupported column types.
if (
$columnDiff->hasChanged('default') &&
count($columnDiff->changedProperties) === 1 &&
($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType)
) {
continue;
}
$columnArray['comment'] = $this->getColumnComment($column);
$queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' '
. $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
}
foreach ($diff->renamedColumns as $oldColumnName => $column) {
if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
continue;
}
$oldColumnName = new Identifier($oldColumnName);
$columnArray = $column->toArray();
$columnArray['comment'] = $this->getColumnComment($column);
$queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' '
. $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray);
}
if (isset($diff->addedIndexes['primary'])) {
$keyColumns = array_unique(array_values($diff->addedIndexes['primary']->getColumns()));
$queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')';
unset($diff->addedIndexes['primary']);
} elseif (isset($diff->changedIndexes['primary'])) {
// Necessary in case the new primary key includes a new auto_increment column
foreach ($diff->changedIndexes['primary']->getColumns() as $columnName) {
if (isset($diff->addedColumns[$columnName]) && $diff->addedColumns[$columnName]->getAutoincrement()) {
$keyColumns = array_unique(array_values($diff->changedIndexes['primary']->getColumns()));
$queryParts[] = 'DROP PRIMARY KEY';
$queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')';
unset($diff->changedIndexes['primary']);
break;
}
}
}
$sql = [];
$tableSql = [];
if (! $this->onSchemaAlterTable($diff, $tableSql)) {
if (count($queryParts) > 0) {
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' '
. implode(', ', $queryParts);
}
$sql = array_merge(
$this->getPreAlterTableIndexForeignKeySQL($diff),
$sql,
$this->getPostAlterTableIndexForeignKeySQL($diff)
);
}
return array_merge($sql, $tableSql, $columnSql);
}
/**
* {@inheritDoc}
*/
protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
{
$sql = [];
$table = $diff->getName($this)->getQuotedName($this);
foreach ($diff->changedIndexes as $changedIndex) {
$sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $changedIndex));
}
foreach ($diff->removedIndexes as $remKey => $remIndex) {
$sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $remIndex));
foreach ($diff->addedIndexes as $addKey => $addIndex) {
if ($remIndex->getColumns() !== $addIndex->getColumns()) {
continue;
}
$indexClause = 'INDEX ' . $addIndex->getName();
if ($addIndex->isPrimary()) {
$indexClause = 'PRIMARY KEY';
} elseif ($addIndex->isUnique()) {
$indexClause = 'UNIQUE INDEX ' . $addIndex->getName();
}
$query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', ';
$query .= 'ADD ' . $indexClause;
$query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex) . ')';
$sql[] = $query;
unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
break;
}
}
$engine = 'INNODB';
if ($diff->fromTable instanceof Table && $diff->fromTable->hasOption('engine')) {
$engine = strtoupper(trim($diff->fromTable->getOption('engine')));
}
// Suppress foreign key constraint propagation on non-supporting engines.
if ($engine !== 'INNODB') {
$diff->addedForeignKeys = [];
$diff->changedForeignKeys = [];
$diff->removedForeignKeys = [];
}
$sql = array_merge(
$sql,
$this->getPreAlterTableAlterIndexForeignKeySQL($diff),
parent::getPreAlterTableIndexForeignKeySQL($diff),
$this->getPreAlterTableRenameIndexForeignKeySQL($diff)
);
return $sql;
}
/**
* @return string[]
*/
private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index)
{
$sql = [];
if (! $index->isPrimary() || ! $diff->fromTable instanceof Table) {
return $sql;
}
$tableName = $diff->getName($this)->getQuotedName($this);
// Dropping primary keys requires to unset autoincrement attribute on the particular column first.
foreach ($index->getColumns() as $columnName) {
if (! $diff->fromTable->hasColumn($columnName)) {
continue;
}
$column = $diff->fromTable->getColumn($columnName);
if ($column->getAutoincrement() !== true) {
continue;
}
$column->setAutoincrement(false);
$sql[] = 'ALTER TABLE ' . $tableName . ' MODIFY ' .
$this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
// original autoincrement information might be needed later on by other parts of the table alteration
$column->setAutoincrement(true);
}
return $sql;
}
/**
* @param TableDiff $diff The table diff to gather the SQL for.
*
* @return string[]
*/
private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff)
{
$sql = [];
$table = $diff->getName($this)->getQuotedName($this);
foreach ($diff->changedIndexes as $changedIndex) {
// Changed primary key
if (! $changedIndex->isPrimary() || ! ($diff->fromTable instanceof Table)) {
continue;
}
foreach ($diff->fromTable->getPrimaryKeyColumns() as $columnName) {
$column = $diff->fromTable->getColumn($columnName);
// Check if an autoincrement column was dropped from the primary key.
if (! $column->getAutoincrement() || in_array($columnName, $changedIndex->getColumns())) {
continue;
}
// The autoincrement attribute needs to be removed from the dropped column
// before we can drop and recreate the primary key.
$column->setAutoincrement(false);
$sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' .
$this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
// Restore the autoincrement attribute as it might be needed later on
// by other parts of the table alteration.
$column->setAutoincrement(true);
}
}
return $sql;
}
/**
* @param TableDiff $diff The table diff to gather the SQL for.
*
* @return string[]
*/
protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
{
$sql = [];
$tableName = $diff->getName($this)->getQuotedName($this);
foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) {
if (in_array($foreignKey, $diff->changedForeignKeys, true)) {
continue;
}
$sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
}
return $sql;
}
/**
* Returns the remaining foreign key constraints that require one of the renamed indexes.
*
* "Remaining" here refers to the diff between the foreign keys currently defined in the associated
* table and the foreign keys to be removed.
*
* @param TableDiff $diff The table diff to evaluate.
*
* @return ForeignKeyConstraint[]
*/
private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff)
{
if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) {
return [];
}
$foreignKeys = [];
/** @var ForeignKeyConstraint[] $remainingForeignKeys */
$remainingForeignKeys = array_diff_key(
$diff->fromTable->getForeignKeys(),
$diff->removedForeignKeys
);
foreach ($remainingForeignKeys as $foreignKey) {
foreach ($diff->renamedIndexes as $index) {
if ($foreignKey->intersectsIndexColumns($index)) {
$foreignKeys[] = $foreignKey;
break;
}
}
}
return $foreignKeys;
}
/**
* {@inheritdoc}
*/
protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
{
return array_merge(
parent::getPostAlterTableIndexForeignKeySQL($diff),
$this->getPostAlterTableRenameIndexForeignKeySQL($diff)
);
}
/**
* @param TableDiff $diff The table diff to gather the SQL for.
*
* @return string[]
*/
protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
{
$sql = [];
$newName = $diff->getNewName();
if ($newName !== false) {
$tableName = $newName->getQuotedName($this);
} else {
$tableName = $diff->getName($this)->getQuotedName($this);
}
foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) {
if (in_array($foreignKey, $diff->changedForeignKeys, true)) {
continue;
}
$sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
}
return $sql;
}
/**
* {@inheritDoc}
*/
protected function getCreateIndexSQLFlags(Index $index)
{
$type = '';
if ($index->isUnique()) {
$type .= 'UNIQUE ';
} elseif ($index->hasFlag('fulltext')) {
$type .= 'FULLTEXT ';
} elseif ($index->hasFlag('spatial')) {
$type .= 'SPATIAL ';
}
return $type;
}
/**
* {@inheritDoc}
*/
public function getIntegerTypeDeclarationSQL(array $column)
{
return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
public function getBigIntTypeDeclarationSQL(array $column)
{
return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritDoc}
*/
public function getSmallIntTypeDeclarationSQL(array $column)
{
return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritdoc}
*/
public function getFloatDeclarationSQL(array $column)
{
return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($column);
}
/**
* {@inheritdoc}
*/
public function getDecimalTypeDeclarationSQL(array $column)
{
return parent::getDecimalTypeDeclarationSQL($column) . $this->getUnsignedDeclaration($column);
}
/**
* Get unsigned declaration for a column.
*
* @param mixed[] $columnDef
*
* @return string
*/
private function getUnsignedDeclaration(array $columnDef)
{
return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : '';
}
/**
* {@inheritDoc}
*/
protected function _getCommonIntegerTypeDeclarationSQL(array $column)
{
$autoinc = '';
if (! empty($column['autoincrement'])) {
$autoinc = ' AUTO_INCREMENT';
}
return $this->getUnsignedDeclaration($column) . $autoinc;
}
/**
* {@inheritDoc}
*/
public function getColumnCharsetDeclarationSQL($charset)
{
return 'CHARACTER SET ' . $charset;
}
/**
* {@inheritDoc}
*/
public function getColumnCollationDeclarationSQL($collation)
{
return 'COLLATE ' . $this->quoteSingleIdentifier($collation);
}
/**
* {@inheritDoc}
*/
public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
{
$query = '';
if ($foreignKey->hasOption('match')) {
$query .= ' MATCH ' . $foreignKey->getOption('match');
}
$query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
return $query;
}
/**
* {@inheritDoc}
*/
public function getDropIndexSQL($index, $table = null)
{
if ($index instanceof Index) {
$indexName = $index->getQuotedName($this);
} elseif (is_string($index)) {
$indexName = $index;
} else {
throw new InvalidArgumentException(
__METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.'
);
}
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
} elseif (! is_string($table)) {
throw new InvalidArgumentException(
__METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.'
);
}
if ($index instanceof Index && $index->isPrimary()) {
// mysql primary keys are always named "PRIMARY",
// so we cannot use them in statements because of them being keyword.
return $this->getDropPrimaryKeySQL($table);
}
return 'DROP INDEX ' . $indexName . ' ON ' . $table;
}
/**
* @param string $table
*
* @return string
*/
protected function getDropPrimaryKeySQL($table)
{
return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
}
/**
* {@inheritDoc}
*/
public function getSetTransactionIsolationSQL($level)
{
return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'mysql';
}
/**
* {@inheritDoc}
*/
public function getReadLockSQL()
{
return 'LOCK IN SHARE MODE';
}
/**
* {@inheritDoc}
*/
protected function initializeDoctrineTypeMappings()
{
$this->doctrineTypeMapping = [
'tinyint' => 'boolean',
'smallint' => 'smallint',
'mediumint' => 'integer',
'int' => 'integer',
'integer' => 'integer',
'bigint' => 'bigint',
'tinytext' => 'text',
'mediumtext' => 'text',
'longtext' => 'text',
'text' => 'text',
'varchar' => 'string',
'string' => 'string',
'char' => 'string',
'date' => 'date',
'datetime' => 'datetime',
'timestamp' => 'datetime',
'time' => 'time',
'float' => 'float',
'double' => 'float',
'real' => 'float',
'decimal' => 'decimal',
'numeric' => 'decimal',
'year' => 'date',
'longblob' => 'blob',
'blob' => 'blob',
'mediumblob' => 'blob',
'tinyblob' => 'blob',
'binary' => 'binary',
'varbinary' => 'binary',
'set' => 'simple_array',
];
}
/**
* {@inheritDoc}
*/
public function getVarcharMaxLength()
{
return 65535;
}
/**
* {@inheritdoc}
*/
public function getBinaryMaxLength()
{
return 65535;
}
/**
* {@inheritDoc}
*/
protected function getReservedKeywordsClass()
{
return Keywords\MySQLKeywords::class;
}
/**
* {@inheritDoc}
*
* MySQL commits a transaction implicitly when DROP TABLE is executed, however not
* if DROP TEMPORARY TABLE is executed.
*/
public function getDropTemporaryTableSQL($table)
{
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
} elseif (! is_string($table)) {
throw new InvalidArgumentException(
__METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.'
);
}
return 'DROP TEMPORARY TABLE ' . $table;
}
/**
* Gets the SQL Snippet used to declare a BLOB column type.
* TINYBLOB : 2 ^ 8 - 1 = 255
* BLOB : 2 ^ 16 - 1 = 65535
* MEDIUMBLOB : 2 ^ 24 - 1 = 16777215
* LONGBLOB : 2 ^ 32 - 1 = 4294967295
*
* {@inheritDoc}
*/
public function getBlobTypeDeclarationSQL(array $column)
{
if (! empty($column['length']) && is_numeric($column['length'])) {
$length = $column['length'];
if ($length <= static::LENGTH_LIMIT_TINYBLOB) {
return 'TINYBLOB';
}
if ($length <= static::LENGTH_LIMIT_BLOB) {
return 'BLOB';
}
if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) {
return 'MEDIUMBLOB';
}
}
return 'LONGBLOB';
}
/**
* {@inheritdoc}
*/
public function quoteStringLiteral($str)
{
$str = str_replace('\\', '\\\\', $str); // MySQL requires backslashes to be escaped aswell.
return parent::quoteStringLiteral($str);
}
/**
* {@inheritdoc}
*/
public function getDefaultTransactionIsolationLevel()
{
return TransactionIsolationLevel::REPEATABLE_READ;
}
public function supportsColumnLengthIndexes(): bool
{
return true;
}
}
lib/Doctrine/DBAL/Platforms/OraclePlatform.php 0000644 00000103662 13727250467 0015202 0 ustar 00 getBitAndComparisonExpression($value1, $value2)
. '+' . $value2 . ')';
}
/**
* {@inheritDoc}
*
* Need to specifiy minvalue, since start with is hidden in the system and MINVALUE <= START WITH.
* Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection
* in {@see listSequences()}
*/
public function getCreateSequenceSQL(Sequence $sequence)
{
return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
' START WITH ' . $sequence->getInitialValue() .
' MINVALUE ' . $sequence->getInitialValue() .
' INCREMENT BY ' . $sequence->getAllocationSize() .
$this->getSequenceCacheSQL($sequence);
}
/**
* {@inheritDoc}
*/
public function getAlterSequenceSQL(Sequence $sequence)
{
return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
' INCREMENT BY ' . $sequence->getAllocationSize()
. $this->getSequenceCacheSQL($sequence);
}
/**
* Cache definition for sequences
*
* @return string
*/
private function getSequenceCacheSQL(Sequence $sequence)
{
if ($sequence->getCache() === 0) {
return ' NOCACHE';
}
if ($sequence->getCache() === 1) {
return ' NOCACHE';
}
if ($sequence->getCache() > 1) {
return ' CACHE ' . $sequence->getCache();
}
return '';
}
/**
* {@inheritDoc}
*/
public function getSequenceNextValSQL($sequence)
{
return 'SELECT ' . $sequence . '.nextval FROM DUAL';
}
/**
* {@inheritDoc}
*/
public function getSetTransactionIsolationSQL($level)
{
return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
}
/**
* {@inheritDoc}
*/
protected function _getTransactionIsolationLevelSQL($level)
{
switch ($level) {
case TransactionIsolationLevel::READ_UNCOMMITTED:
return 'READ UNCOMMITTED';
case TransactionIsolationLevel::READ_COMMITTED:
return 'READ COMMITTED';
case TransactionIsolationLevel::REPEATABLE_READ:
case TransactionIsolationLevel::SERIALIZABLE:
return 'SERIALIZABLE';
default:
return parent::_getTransactionIsolationLevelSQL($level);
}
}
/**
* {@inheritDoc}
*/
public function getBooleanTypeDeclarationSQL(array $column)
{
return 'NUMBER(1)';
}
/**
* {@inheritDoc}
*/
public function getIntegerTypeDeclarationSQL(array $column)
{
return 'NUMBER(10)';
}
/**
* {@inheritDoc}
*/
public function getBigIntTypeDeclarationSQL(array $column)
{
return 'NUMBER(20)';
}
/**
* {@inheritDoc}
*/
public function getSmallIntTypeDeclarationSQL(array $column)
{
return 'NUMBER(5)';
}
/**
* {@inheritDoc}
*/
public function getDateTimeTypeDeclarationSQL(array $column)
{
return 'TIMESTAMP(0)';
}
/**
* {@inheritDoc}
*/
public function getDateTimeTzTypeDeclarationSQL(array $column)
{
return 'TIMESTAMP(0) WITH TIME ZONE';
}
/**
* {@inheritDoc}
*/
public function getDateTypeDeclarationSQL(array $column)
{
return 'DATE';
}
/**
* {@inheritDoc}
*/
public function getTimeTypeDeclarationSQL(array $column)
{
return 'DATE';
}
/**
* {@inheritDoc}
*/
protected function _getCommonIntegerTypeDeclarationSQL(array $column)
{
return '';
}
/**
* {@inheritDoc}
*/
protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
{
return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(2000)')
: ($length ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)');
}
/**
* {@inheritdoc}
*/
protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
{
return 'RAW(' . ($length ?: $this->getBinaryMaxLength()) . ')';
}
/**
* {@inheritdoc}
*/
public function getBinaryMaxLength()
{
return 2000;
}
/**
* {@inheritDoc}
*/
public function getClobTypeDeclarationSQL(array $column)
{
return 'CLOB';
}
/**
* {@inheritDoc}
*/
public function getListDatabasesSQL()
{
return 'SELECT username FROM all_users';
}
/**
* {@inheritDoc}
*/
public function getListSequencesSQL($database)
{
$database = $this->normalizeIdentifier($database);
$database = $this->quoteStringLiteral($database->getName());
return 'SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ' .
'WHERE SEQUENCE_OWNER = ' . $database;
}
/**
* {@inheritDoc}
*/
protected function _getCreateTableSQL($name, array $columns, array $options = [])
{
$indexes = $options['indexes'] ?? [];
$options['indexes'] = [];
$sql = parent::_getCreateTableSQL($name, $columns, $options);
foreach ($columns as $columnName => $column) {
if (isset($column['sequence'])) {
$sql[] = $this->getCreateSequenceSQL($column['sequence']);
}
if (
! isset($column['autoincrement']) || ! $column['autoincrement'] &&
(! isset($column['autoinc']) || ! $column['autoinc'])
) {
continue;
}
$sql = array_merge($sql, $this->getCreateAutoincrementSql($columnName, $name));
}
if (isset($indexes) && ! empty($indexes)) {
foreach ($indexes as $index) {
$sql[] = $this->getCreateIndexSQL($index, $name);
}
}
return $sql;
}
/**
* {@inheritDoc}
*
* @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html
*/
public function getListTableIndexesSQL($table, $database = null)
{
$table = $this->normalizeIdentifier($table);
$table = $this->quoteStringLiteral($table->getName());
return "SELECT uind_col.index_name AS name,
(
SELECT uind.index_type
FROM user_indexes uind
WHERE uind.index_name = uind_col.index_name
) AS type,
decode(
(
SELECT uind.uniqueness
FROM user_indexes uind
WHERE uind.index_name = uind_col.index_name
),
'NONUNIQUE',
0,
'UNIQUE',
1
) AS is_unique,
uind_col.column_name AS column_name,
uind_col.column_position AS column_pos,
(
SELECT ucon.constraint_type
FROM user_constraints ucon
WHERE ucon.index_name = uind_col.index_name
) AS is_primary
FROM user_ind_columns uind_col
WHERE uind_col.table_name = " . $table . '
ORDER BY uind_col.column_position ASC';
}
/**
* {@inheritDoc}
*/
public function getListTablesSQL()
{
return 'SELECT * FROM sys.user_tables';
}
/**
* {@inheritDoc}
*/
public function getListViewsSQL($database)
{
return 'SELECT view_name, text FROM sys.user_views';
}
/**
* {@inheritDoc}
*/
public function getCreateViewSQL($name, $sql)
{
return 'CREATE VIEW ' . $name . ' AS ' . $sql;
}
/**
* {@inheritDoc}
*/
public function getDropViewSQL($name)
{
return 'DROP VIEW ' . $name;
}
/**
* @param string $name
* @param string $table
* @param int $start
*
* @return string[]
*/
public function getCreateAutoincrementSql($name, $table, $start = 1)
{
$tableIdentifier = $this->normalizeIdentifier($table);
$quotedTableName = $tableIdentifier->getQuotedName($this);
$unquotedTableName = $tableIdentifier->getName();
$nameIdentifier = $this->normalizeIdentifier($name);
$quotedName = $nameIdentifier->getQuotedName($this);
$unquotedName = $nameIdentifier->getName();
$sql = [];
$autoincrementIdentifierName = $this->getAutoincrementIdentifierName($tableIdentifier);
$idx = new Index($autoincrementIdentifierName, [$quotedName], true, true);
$sql[] = "DECLARE
constraints_Count NUMBER;
BEGIN
SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = '" . $unquotedTableName
. "' AND CONSTRAINT_TYPE = 'P';
IF constraints_Count = 0 OR constraints_Count = '' THEN
EXECUTE IMMEDIATE '" . $this->getCreateConstraintSQL($idx, $quotedTableName) . "';
END IF;
END;";
$sequenceName = $this->getIdentitySequenceName(
$tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName,
$nameIdentifier->isQuoted() ? $quotedName : $unquotedName
);
$sequence = new Sequence($sequenceName, $start);
$sql[] = $this->getCreateSequenceSQL($sequence);
$sql[] = 'CREATE TRIGGER ' . $autoincrementIdentifierName . '
BEFORE INSERT
ON ' . $quotedTableName . '
FOR EACH ROW
DECLARE
last_Sequence NUMBER;
last_InsertID NUMBER;
BEGIN
SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL;
IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.' . $quotedName . ' = 0) THEN
SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL;
ELSE
SELECT NVL(Last_Number, 0) INTO last_Sequence
FROM User_Sequences
WHERE Sequence_Name = \'' . $sequence->getName() . '\';
SELECT :NEW.' . $quotedName . ' INTO last_InsertID FROM DUAL;
WHILE (last_InsertID > last_Sequence) LOOP
SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL;
END LOOP;
END IF;
END;';
return $sql;
}
/**
* Returns the SQL statements to drop the autoincrement for the given table name.
*
* @param string $table The table name to drop the autoincrement for.
*
* @return string[]
*/
public function getDropAutoincrementSql($table)
{
$table = $this->normalizeIdentifier($table);
$autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table);
$identitySequenceName = $this->getIdentitySequenceName(
$table->isQuoted() ? $table->getQuotedName($this) : $table->getName(),
''
);
return [
'DROP TRIGGER ' . $autoincrementIdentifierName,
$this->getDropSequenceSQL($identitySequenceName),
$this->getDropConstraintSQL($autoincrementIdentifierName, $table->getQuotedName($this)),
];
}
/**
* Normalizes the given identifier.
*
* Uppercases the given identifier if it is not quoted by intention
* to reflect Oracle's internal auto uppercasing strategy of unquoted identifiers.
*
* @param string $name The identifier to normalize.
*
* @return Identifier The normalized identifier.
*/
private function normalizeIdentifier($name)
{
$identifier = new Identifier($name);
return $identifier->isQuoted() ? $identifier : new Identifier(strtoupper($name));
}
/**
* Adds suffix to identifier,
*
* if the new string exceeds max identifier length,
* keeps $suffix, cuts from $identifier as much as the part exceeding.
*/
private function addSuffix(string $identifier, string $suffix): string
{
$maxPossibleLengthWithoutSuffix = $this->getMaxIdentifierLength() - strlen($suffix);
if (strlen($identifier) > $maxPossibleLengthWithoutSuffix) {
$identifier = substr($identifier, 0, $maxPossibleLengthWithoutSuffix);
}
return $identifier . $suffix;
}
/**
* Returns the autoincrement primary key identifier name for the given table identifier.
*
* Quotes the autoincrement primary key identifier name
* if the given table name is quoted by intention.
*
* @param Identifier $table The table identifier to return the autoincrement primary key identifier name for.
*
* @return string
*/
private function getAutoincrementIdentifierName(Identifier $table)
{
$identifierName = $this->addSuffix($table->getName(), '_AI_PK');
return $table->isQuoted()
? $this->quoteSingleIdentifier($identifierName)
: $identifierName;
}
/**
* {@inheritDoc}
*/
public function getListTableForeignKeysSQL($table)
{
$table = $this->normalizeIdentifier($table);
$table = $this->quoteStringLiteral($table->getName());
return "SELECT alc.constraint_name,
alc.DELETE_RULE,
cols.column_name \"local_column\",
cols.position,
(
SELECT r_cols.table_name
FROM user_cons_columns r_cols
WHERE alc.r_constraint_name = r_cols.constraint_name
AND r_cols.position = cols.position
) AS \"references_table\",
(
SELECT r_cols.column_name
FROM user_cons_columns r_cols
WHERE alc.r_constraint_name = r_cols.constraint_name
AND r_cols.position = cols.position
) AS \"foreign_column\"
FROM user_cons_columns cols
JOIN user_constraints alc
ON alc.constraint_name = cols.constraint_name
AND alc.constraint_type = 'R'
AND alc.table_name = " . $table . '
ORDER BY cols.constraint_name ASC, cols.position ASC';
}
/**
* {@inheritDoc}
*/
public function getListTableConstraintsSQL($table)
{
$table = $this->normalizeIdentifier($table);
$table = $this->quoteStringLiteral($table->getName());
return 'SELECT * FROM user_constraints WHERE table_name = ' . $table;
}
/**
* {@inheritDoc}
*/
public function getListTableColumnsSQL($table, $database = null)
{
$table = $this->normalizeIdentifier($table);
$table = $this->quoteStringLiteral($table->getName());
$tabColumnsTableName = 'user_tab_columns';
$colCommentsTableName = 'user_col_comments';
$tabColumnsOwnerCondition = '';
$colCommentsOwnerCondition = '';
if ($database !== null && $database !== '/') {
$database = $this->normalizeIdentifier($database);
$database = $this->quoteStringLiteral($database->getName());
$tabColumnsTableName = 'all_tab_columns';
$colCommentsTableName = 'all_col_comments';
$tabColumnsOwnerCondition = ' AND c.owner = ' . $database;
$colCommentsOwnerCondition = ' AND d.OWNER = c.OWNER';
}
return sprintf(
<<<'SQL'
SELECT c.*,
(
SELECT d.comments
FROM %s d
WHERE d.TABLE_NAME = c.TABLE_NAME%s
AND d.COLUMN_NAME = c.COLUMN_NAME
) AS comments
FROM %s c
WHERE c.table_name = %s%s
ORDER BY c.column_id
SQL
,
$colCommentsTableName,
$colCommentsOwnerCondition,
$tabColumnsTableName,
$table,
$tabColumnsOwnerCondition
);
}
/**
* {@inheritDoc}
*/
public function getDropSequenceSQL($sequence)
{
if ($sequence instanceof Sequence) {
$sequence = $sequence->getQuotedName($this);
}
return 'DROP SEQUENCE ' . $sequence;
}
/**
* {@inheritDoc}
*/
public function getDropForeignKeySQL($foreignKey, $table)
{
if (! $foreignKey instanceof ForeignKeyConstraint) {
$foreignKey = new Identifier($foreignKey);
}
if (! $table instanceof Table) {
$table = new Identifier($table);
}
$foreignKey = $foreignKey->getQuotedName($this);
$table = $table->getQuotedName($this);
return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey;
}
/**
* {@inheritdoc}
*/
public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
{
$referentialAction = null;
if ($foreignKey->hasOption('onDelete')) {
$referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
}
return $referentialAction ? ' ON DELETE ' . $referentialAction : '';
}
/**
* {@inheritdoc}
*/
public function getForeignKeyReferentialActionSQL($action)
{
$action = strtoupper($action);
switch ($action) {
case 'RESTRICT': // RESTRICT is not supported, therefore falling back to NO ACTION.
case 'NO ACTION':
// NO ACTION cannot be declared explicitly,
// therefore returning empty string to indicate to OMIT the referential clause.
return '';
case 'CASCADE':
case 'SET NULL':
return $action;
default:
// SET DEFAULT is not supported, throw exception instead.
throw new InvalidArgumentException('Invalid foreign key action: ' . $action);
}
}
/**
* {@inheritDoc}
*/
public function getDropDatabaseSQL($database)
{
return 'DROP USER ' . $database . ' CASCADE';
}
/**
* {@inheritDoc}
*/
public function getAlterTableSQL(TableDiff $diff)
{
$sql = [];
$commentsSQL = [];
$columnSql = [];
$fields = [];
foreach ($diff->addedColumns as $column) {
if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
continue;
}
$fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
$comment = $this->getColumnComment($column);
if (! $comment) {
continue;
}
$commentsSQL[] = $this->getCommentOnColumnSQL(
$diff->getName($this)->getQuotedName($this),
$column->getQuotedName($this),
$comment
);
}
if (count($fields)) {
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this)
. ' ADD (' . implode(', ', $fields) . ')';
}
$fields = [];
foreach ($diff->changedColumns as $columnDiff) {
if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
continue;
}
$column = $columnDiff->column;
// Do not generate column alteration clause if type is binary and only fixed property has changed.
// Oracle only supports binary type columns with variable length.
// Avoids unnecessary table alteration statements.
if (
$column->getType() instanceof BinaryType &&
$columnDiff->hasChanged('fixed') &&
count($columnDiff->changedProperties) === 1
) {
continue;
}
$columnHasChangedComment = $columnDiff->hasChanged('comment');
/**
* Do not add query part if only comment has changed
*/
if (! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) {
$columnInfo = $column->toArray();
if (! $columnDiff->hasChanged('notnull')) {
unset($columnInfo['notnull']);
}
$fields[] = $column->getQuotedName($this) . $this->getColumnDeclarationSQL('', $columnInfo);
}
if (! $columnHasChangedComment) {
continue;
}
$commentsSQL[] = $this->getCommentOnColumnSQL(
$diff->getName($this)->getQuotedName($this),
$column->getQuotedName($this),
$this->getColumnComment($column)
);
}
if (count($fields)) {
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this)
. ' MODIFY (' . implode(', ', $fields) . ')';
}
foreach ($diff->renamedColumns as $oldColumnName => $column) {
if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
continue;
}
$oldColumnName = new Identifier($oldColumnName);
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) .
' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this);
}
$fields = [];
foreach ($diff->removedColumns as $column) {
if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
continue;
}
$fields[] = $column->getQuotedName($this);
}
if (count($fields)) {
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this)
. ' DROP (' . implode(', ', $fields) . ')';
}
$tableSql = [];
if (! $this->onSchemaAlterTable($diff, $tableSql)) {
$sql = array_merge($sql, $commentsSQL);
$newName = $diff->getNewName();
if ($newName !== false) {
$sql[] = sprintf(
'ALTER TABLE %s RENAME TO %s',
$diff->getName($this)->getQuotedName($this),
$newName->getQuotedName($this)
);
}
$sql = array_merge(
$this->getPreAlterTableIndexForeignKeySQL($diff),
$sql,
$this->getPostAlterTableIndexForeignKeySQL($diff)
);
}
return array_merge($sql, $tableSql, $columnSql);
}
/**
* {@inheritdoc}
*/
public function getColumnDeclarationSQL($name, array $column)
{
if (isset($column['columnDefinition'])) {
$columnDef = $this->getCustomTypeDeclarationSQL($column);
} else {
$default = $this->getDefaultValueDeclarationSQL($column);
$notnull = '';
if (isset($column['notnull'])) {
$notnull = $column['notnull'] ? ' NOT NULL' : ' NULL';
}
$unique = isset($column['unique']) && $column['unique'] ?
' ' . $this->getUniqueFieldDeclarationSQL() : '';
$check = isset($column['check']) && $column['check'] ?
' ' . $column['check'] : '';
$typeDecl = $column['type']->getSQLDeclaration($column, $this);
$columnDef = $typeDecl . $default . $notnull . $unique . $check;
}
return $name . ' ' . $columnDef;
}
/**
* {@inheritdoc}
*/
protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
{
if (strpos($tableName, '.') !== false) {
[$schema] = explode('.', $tableName);
$oldIndexName = $schema . '.' . $oldIndexName;
}
return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)];
}
/**
* {@inheritDoc}
*/
public function prefersSequences()
{
return true;
}
/**
* {@inheritdoc}
*/
public function usesSequenceEmulatedIdentityColumns()
{
return true;
}
/**
* {@inheritdoc}
*/
public function getIdentitySequenceName($tableName, $columnName)
{
$table = new Identifier($tableName);
// No usage of column name to preserve BC compatibility with <2.5
$identitySequenceName = $this->addSuffix($table->getName(), '_SEQ');
if ($table->isQuoted()) {
$identitySequenceName = '"' . $identitySequenceName . '"';
}
$identitySequenceIdentifier = $this->normalizeIdentifier($identitySequenceName);
return $identitySequenceIdentifier->getQuotedName($this);
}
/**
* {@inheritDoc}
*/
public function supportsCommentOnStatement()
{
return true;
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'oracle';
}
/**
* {@inheritDoc}
*/
protected function doModifyLimitQuery($query, $limit, $offset = null)
{
if ($limit === null && $offset <= 0) {
return $query;
}
if (preg_match('/^\s*SELECT/i', $query)) {
if (! preg_match('/\sFROM\s/i', $query)) {
$query .= ' FROM dual';
}
$columns = ['a.*'];
if ($offset > 0) {
$columns[] = 'ROWNUM AS doctrine_rownum';
}
$query = sprintf('SELECT %s FROM (%s) a', implode(', ', $columns), $query);
if ($limit !== null) {
$query .= sprintf(' WHERE ROWNUM <= %d', $offset + $limit);
}
if ($offset > 0) {
$query = sprintf('SELECT * FROM (%s) WHERE doctrine_rownum >= %d', $query, $offset + 1);
}
}
return $query;
}
/**
* {@inheritDoc}
*
* Oracle returns all column names in SQL result sets in uppercase.
*/
public function getSQLResultCasing($column)
{
return strtoupper($column);
}
/**
* {@inheritDoc}
*/
public function getCreateTemporaryTableSnippetSQL()
{
return 'CREATE GLOBAL TEMPORARY TABLE';
}
/**
* {@inheritDoc}
*/
public function getDateTimeTzFormatString()
{
return 'Y-m-d H:i:sP';
}
/**
* {@inheritDoc}
*/
public function getDateFormatString()
{
return 'Y-m-d 00:00:00';
}
/**
* {@inheritDoc}
*/
public function getTimeFormatString()
{
return '1900-01-01 H:i:s';
}
/**
* {@inheritDoc}
*/
public function fixSchemaElementName($schemaElementName)
{
if (strlen($schemaElementName) > 30) {
// Trim it
return substr($schemaElementName, 0, 30);
}
return $schemaElementName;
}
/**
* {@inheritDoc}
*/
public function getMaxIdentifierLength()
{
return 30;
}
/**
* {@inheritDoc}
*/
public function supportsSequences()
{
return true;
}
/**
* {@inheritDoc}
*/
public function supportsForeignKeyOnUpdate()
{
return false;
}
/**
* {@inheritDoc}
*/
public function supportsReleaseSavepoints()
{
return false;
}
/**
* {@inheritDoc}
*/
public function getTruncateTableSQL($tableName, $cascade = false)
{
$tableIdentifier = new Identifier($tableName);
return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this);
}
/**
* {@inheritDoc}
*/
public function getDummySelectSQL()
{
$expression = func_num_args() > 0 ? func_get_arg(0) : '1';
return sprintf('SELECT %s FROM DUAL', $expression);
}
/**
* {@inheritDoc}
*/
protected function initializeDoctrineTypeMappings()
{
$this->doctrineTypeMapping = [
'integer' => 'integer',
'number' => 'integer',
'pls_integer' => 'boolean',
'binary_integer' => 'boolean',
'varchar' => 'string',
'varchar2' => 'string',
'nvarchar2' => 'string',
'char' => 'string',
'nchar' => 'string',
'date' => 'date',
'timestamp' => 'datetime',
'timestamptz' => 'datetimetz',
'float' => 'float',
'binary_float' => 'float',
'binary_double' => 'float',
'long' => 'string',
'clob' => 'text',
'nclob' => 'text',
'raw' => 'binary',
'long raw' => 'blob',
'rowid' => 'string',
'urowid' => 'string',
'blob' => 'blob',
];
}
/**
* {@inheritDoc}
*/
public function releaseSavePoint($savepoint)
{
return '';
}
/**
* {@inheritDoc}
*/
protected function getReservedKeywordsClass()
{
return Keywords\OracleKeywords::class;
}
/**
* {@inheritDoc}
*/
public function getBlobTypeDeclarationSQL(array $column)
{
return 'BLOB';
}
public function getListTableCommentsSQL(string $table, ?string $database = null): string
{
$tableCommentsName = 'user_tab_comments';
$ownerCondition = '';
if ($database !== null && $database !== '/') {
$tableCommentsName = 'all_tab_comments';
$ownerCondition = ' AND owner = ' . $this->quoteStringLiteral(
$this->normalizeIdentifier($database)->getName()
);
}
return sprintf(
<<<'SQL'
SELECT comments FROM %s WHERE table_name = %s%s
SQL
,
$tableCommentsName,
$this->quoteStringLiteral($this->normalizeIdentifier($table)->getName()),
$ownerCondition
);
}
}
lib/Doctrine/DBAL/Platforms/PostgreSQL100Platform.php 0000644 00000001724 13727250467 0016215 0 ustar 00 quoteStringLiteral($database) . "
AND sequence_schema NOT LIKE 'pg\_%'
AND sequence_schema != 'information_schema'";
}
}
lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php 0000644 00000002127 13727250467 0016144 0 ustar 00 quoteSingleIdentifier($collation);
}
/**
* {@inheritDoc}
*/
public function getListTableColumnsSQL($table, $database = null)
{
$sql = parent::getListTableColumnsSQL($table, $database);
$parts = explode('AS complete_type,', $sql, 2);
return $parts[0] . 'AS complete_type, '
. '(SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation,'
. $parts[1];
}
}
lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php 0000644 00000002631 13727250467 0016145 0 ustar 00 doctrineTypeMapping['json'] = Types::JSON;
}
/**
* {@inheritdoc}
*/
public function getCloseActiveDatabaseConnectionsSQL($database)
{
return sprintf(
'SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = %s',
$this->quoteStringLiteral($database)
);
}
}
lib/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php 0000644 00000001461 13727250467 0016147 0 ustar 00 doctrineTypeMapping['jsonb'] = Types::JSON;
}
}
lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php 0000644 00000110112 13727250467 0016064 0 ustar 00 [
't',
'true',
'y',
'yes',
'on',
'1',
],
'false' => [
'f',
'false',
'n',
'no',
'off',
'0',
],
];
/**
* PostgreSQL has different behavior with some drivers
* with regard to how booleans have to be handled.
*
* Enables use of 'true'/'false' or otherwise 1 and 0 instead.
*
* @param bool $flag
*
* @return void
*/
public function setUseBooleanTrueFalseStrings($flag)
{
$this->useBooleanTrueFalseStrings = (bool) $flag;
}
/**
* {@inheritDoc}
*/
public function getSubstringExpression($string, $start, $length = null)
{
if ($length === null) {
return 'SUBSTRING(' . $string . ' FROM ' . $start . ')';
}
return 'SUBSTRING(' . $string . ' FROM ' . $start . ' FOR ' . $length . ')';
}
/**
* {@inheritDoc}
*/
public function getNowExpression()
{
return 'LOCALTIMESTAMP(0)';
}
/**
* {@inheritDoc}
*/
public function getRegexpExpression()
{
return 'SIMILAR TO';
}
/**
* {@inheritDoc}
*/
public function getLocateExpression($str, $substr, $startPos = false)
{
if ($startPos !== false) {
$str = $this->getSubstringExpression($str, $startPos);
return 'CASE WHEN (POSITION(' . $substr . ' IN ' . $str . ') = 0) THEN 0'
. ' ELSE (POSITION(' . $substr . ' IN ' . $str . ') + ' . ($startPos - 1) . ') END';
}
return 'POSITION(' . $substr . ' IN ' . $str . ')';
}
/**
* {@inheritdoc}
*/
protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
{
if ($unit === DateIntervalUnit::QUARTER) {
$interval *= 3;
$unit = DateIntervalUnit::MONTH;
}
return '(' . $date . ' ' . $operator . ' (' . $interval . " || ' " . $unit . "')::interval)";
}
/**
* {@inheritDoc}
*/
public function getDateDiffExpression($date1, $date2)
{
return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))';
}
/**
* {@inheritDoc}
*/
public function supportsSequences()
{
return true;
}
/**
* {@inheritDoc}
*/
public function supportsSchemas()
{
return true;
}
/**
* {@inheritdoc}
*/
public function getDefaultSchemaName()
{
return 'public';
}
/**
* {@inheritDoc}
*/
public function supportsIdentityColumns()
{
return true;
}
/**
* {@inheritdoc}
*/
public function supportsPartialIndexes()
{
return true;
}
/**
* {@inheritdoc}
*/
public function usesSequenceEmulatedIdentityColumns()
{
return true;
}
/**
* {@inheritdoc}
*/
public function getIdentitySequenceName($tableName, $columnName)
{
return $tableName . '_' . $columnName . '_seq';
}
/**
* {@inheritDoc}
*/
public function supportsCommentOnStatement()
{
return true;
}
/**
* {@inheritDoc}
*/
public function prefersSequences()
{
return true;
}
/**
* {@inheritDoc}
*/
public function hasNativeGuidType()
{
return true;
}
/**
* {@inheritDoc}
*/
public function getListDatabasesSQL()
{
return 'SELECT datname FROM pg_database';
}
/**
* {@inheritDoc}
*/
public function getListNamespacesSQL()
{
return "SELECT schema_name AS nspname
FROM information_schema.schemata
WHERE schema_name NOT LIKE 'pg\_%'
AND schema_name != 'information_schema'";
}
/**
* {@inheritDoc}
*/
public function getListSequencesSQL($database)
{
return "SELECT sequence_name AS relname,
sequence_schema AS schemaname
FROM information_schema.sequences
WHERE sequence_schema NOT LIKE 'pg\_%'
AND sequence_schema != 'information_schema'";
}
/**
* {@inheritDoc}
*/
public function getListTablesSQL()
{
return "SELECT quote_ident(table_name) AS table_name,
table_schema AS schema_name
FROM information_schema.tables
WHERE table_schema NOT LIKE 'pg\_%'
AND table_schema != 'information_schema'
AND table_name != 'geometry_columns'
AND table_name != 'spatial_ref_sys'
AND table_type != 'VIEW'";
}
/**
* {@inheritDoc}
*/
public function getListViewsSQL($database)
{
return 'SELECT quote_ident(table_name) AS viewname,
table_schema AS schemaname,
view_definition AS definition
FROM information_schema.views
WHERE view_definition IS NOT NULL';
}
/**
* @param string $table
* @param string|null $database
*
* @return string
*/
public function getListTableForeignKeysSQL($table, $database = null)
{
return 'SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef
FROM pg_catalog.pg_constraint r
WHERE r.conrelid =
(
SELECT c.oid
FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n
WHERE ' . $this->getTableWhereClause($table) . " AND n.oid = c.relnamespace
)
AND r.contype = 'f'";
}
/**
* {@inheritDoc}
*/
public function getCreateViewSQL($name, $sql)
{
return 'CREATE VIEW ' . $name . ' AS ' . $sql;
}
/**
* {@inheritDoc}
*/
public function getDropViewSQL($name)
{
return 'DROP VIEW ' . $name;
}
/**
* {@inheritDoc}
*/
public function getListTableConstraintsSQL($table)
{
$table = new Identifier($table);
$table = $this->quoteStringLiteral($table->getName());
return sprintf(
<<<'SQL'
SELECT
quote_ident(relname) as relname
FROM
pg_class
WHERE oid IN (
SELECT indexrelid
FROM pg_index, pg_class
WHERE pg_class.relname = %s
AND pg_class.oid = pg_index.indrelid
AND (indisunique = 't' OR indisprimary = 't')
)
SQL
,
$table
);
}
/**
* {@inheritDoc}
*
* @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
*/
public function getListTableIndexesSQL($table, $database = null)
{
return 'SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary,
pg_index.indkey, pg_index.indrelid,
pg_get_expr(indpred, indrelid) AS where
FROM pg_class, pg_index
WHERE oid IN (
SELECT indexrelid
FROM pg_index si, pg_class sc, pg_namespace sn
WHERE ' . $this->getTableWhereClause($table, 'sc', 'sn') . '
AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid
) AND pg_index.indexrelid = oid';
}
/**
* @param string $table
* @param string $classAlias
* @param string $namespaceAlias
*
* @return string
*/
private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n')
{
$whereClause = $namespaceAlias . ".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND ";
if (strpos($table, '.') !== false) {
[$schema, $table] = explode('.', $table);
$schema = $this->quoteStringLiteral($schema);
} else {
$schema = 'ANY(current_schemas(false))';
}
$table = new Identifier($table);
$table = $this->quoteStringLiteral($table->getName());
return $whereClause . sprintf(
'%s.relname = %s AND %s.nspname = %s',
$classAlias,
$table,
$namespaceAlias,
$schema
);
}
/**
* {@inheritDoc}
*/
public function getListTableColumnsSQL($table, $database = null)
{
return "SELECT
a.attnum,
quote_ident(a.attname) AS field,
t.typname AS type,
format_type(a.atttypid, a.atttypmod) AS complete_type,
(SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type,
(SELECT format_type(t2.typbasetype, t2.typtypmod) FROM
pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type,
a.attnotnull AS isnotnull,
(SELECT 't'
FROM pg_index
WHERE c.oid = pg_index.indrelid
AND pg_index.indkey[0] = a.attnum
AND pg_index.indisprimary = 't'
) AS pri,
(SELECT pg_get_expr(adbin, adrelid)
FROM pg_attrdef
WHERE c.oid = pg_attrdef.adrelid
AND pg_attrdef.adnum=a.attnum
) AS default,
(SELECT pg_description.description
FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid
) AS comment
FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n
WHERE " . $this->getTableWhereClause($table, 'c', 'n') . '
AND a.attnum > 0
AND a.attrelid = c.oid
AND a.atttypid = t.oid
AND n.oid = c.relnamespace
ORDER BY a.attnum';
}
/**
* {@inheritDoc}
*/
public function getCreateDatabaseSQL($name)
{
return 'CREATE DATABASE ' . $name;
}
/**
* Returns the SQL statement for disallowing new connections on the given database.
*
* This is useful to force DROP DATABASE operations which could fail because of active connections.
*
* @param string $database The name of the database to disallow new connections for.
*
* @return string
*/
public function getDisallowDatabaseConnectionsSQL($database)
{
return "UPDATE pg_database SET datallowconn = 'false' WHERE datname = " . $this->quoteStringLiteral($database);
}
/**
* Returns the SQL statement for closing currently active connections on the given database.
*
* This is useful to force DROP DATABASE operations which could fail because of active connections.
*
* @param string $database The name of the database to close currently active connections for.
*
* @return string
*/
public function getCloseActiveDatabaseConnectionsSQL($database)
{
return 'SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = '
. $this->quoteStringLiteral($database);
}
/**
* {@inheritDoc}
*/
public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
{
$query = '';
if ($foreignKey->hasOption('match')) {
$query .= ' MATCH ' . $foreignKey->getOption('match');
}
$query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) {
$query .= ' DEFERRABLE';
} else {
$query .= ' NOT DEFERRABLE';
}
if (
($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false)
|| ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false)
) {
$query .= ' INITIALLY DEFERRED';
} else {
$query .= ' INITIALLY IMMEDIATE';
}
return $query;
}
/**
* {@inheritDoc}
*/
public function getAlterTableSQL(TableDiff $diff)
{
$sql = [];
$commentsSQL = [];
$columnSql = [];
foreach ($diff->addedColumns as $column) {
if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
continue;
}
$query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
$comment = $this->getColumnComment($column);
if ($comment === null || $comment === '') {
continue;
}
$commentsSQL[] = $this->getCommentOnColumnSQL(
$diff->getName($this)->getQuotedName($this),
$column->getQuotedName($this),
$comment
);
}
foreach ($diff->removedColumns as $column) {
if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
continue;
}
$query = 'DROP ' . $column->getQuotedName($this);
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
}
foreach ($diff->changedColumns as $columnDiff) {
if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
continue;
}
if ($this->isUnchangedBinaryColumn($columnDiff)) {
continue;
}
$oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this);
$column = $columnDiff->column;
if (
$columnDiff->hasChanged('type')
|| $columnDiff->hasChanged('precision')
|| $columnDiff->hasChanged('scale')
|| $columnDiff->hasChanged('fixed')
) {
$type = $column->getType();
// SERIAL/BIGSERIAL are not "real" types and we can't alter a column to that type
$columnDefinition = $column->toArray();
$columnDefinition['autoincrement'] = false;
// here was a server version check before, but DBAL API does not support this anymore.
$query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSQLDeclaration($columnDefinition, $this);
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
}
if ($columnDiff->hasChanged('default') || $this->typeChangeBreaksDefaultValue($columnDiff)) {
$defaultClause = $column->getDefault() === null
? ' DROP DEFAULT'
: ' SET' . $this->getDefaultValueDeclarationSQL($column->toArray());
$query = 'ALTER ' . $oldColumnName . $defaultClause;
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
}
if ($columnDiff->hasChanged('notnull')) {
$query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotnull() ? 'SET' : 'DROP') . ' NOT NULL';
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
}
if ($columnDiff->hasChanged('autoincrement')) {
if ($column->getAutoincrement()) {
// add autoincrement
$seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName);
$sql[] = 'CREATE SEQUENCE ' . $seqName;
$sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ') FROM '
. $diff->getName($this)->getQuotedName($this) . '))';
$query = 'ALTER ' . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')";
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
} else {
// Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have
$query = 'ALTER ' . $oldColumnName . ' DROP DEFAULT';
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
}
}
$newComment = $this->getColumnComment($column);
$oldComment = $this->getOldColumnComment($columnDiff);
if (
$columnDiff->hasChanged('comment')
|| ($columnDiff->fromColumn !== null && $oldComment !== $newComment)
) {
$commentsSQL[] = $this->getCommentOnColumnSQL(
$diff->getName($this)->getQuotedName($this),
$column->getQuotedName($this),
$newComment
);
}
if (! $columnDiff->hasChanged('length')) {
continue;
}
$query = 'ALTER ' . $oldColumnName . ' TYPE '
. $column->getType()->getSQLDeclaration($column->toArray(), $this);
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
}
foreach ($diff->renamedColumns as $oldColumnName => $column) {
if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
continue;
}
$oldColumnName = new Identifier($oldColumnName);
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) .
' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this);
}
$tableSql = [];
if (! $this->onSchemaAlterTable($diff, $tableSql)) {
$sql = array_merge($sql, $commentsSQL);
$newName = $diff->getNewName();
if ($newName !== false) {
$sql[] = sprintf(
'ALTER TABLE %s RENAME TO %s',
$diff->getName($this)->getQuotedName($this),
$newName->getQuotedName($this)
);
}
$sql = array_merge(
$this->getPreAlterTableIndexForeignKeySQL($diff),
$sql,
$this->getPostAlterTableIndexForeignKeySQL($diff)
);
}
return array_merge($sql, $tableSql, $columnSql);
}
/**
* Checks whether a given column diff is a logically unchanged binary type column.
*
* Used to determine whether a column alteration for a binary type column can be skipped.
* Doctrine's {@link BinaryType} and {@link BlobType} are mapped to the same database column type on this platform
* as this platform does not have a native VARBINARY/BINARY column type. Therefore the comparator
* might detect differences for binary type columns which do not have to be propagated
* to database as there actually is no difference at database level.
*
* @param ColumnDiff $columnDiff The column diff to check against.
*
* @return bool True if the given column diff is an unchanged binary type column, false otherwise.
*/
private function isUnchangedBinaryColumn(ColumnDiff $columnDiff)
{
$columnType = $columnDiff->column->getType();
if (! $columnType instanceof BinaryType && ! $columnType instanceof BlobType) {
return false;
}
$fromColumn = $columnDiff->fromColumn instanceof Column ? $columnDiff->fromColumn : null;
if ($fromColumn) {
$fromColumnType = $fromColumn->getType();
if (! $fromColumnType instanceof BinaryType && ! $fromColumnType instanceof BlobType) {
return false;
}
return count(array_diff($columnDiff->changedProperties, ['type', 'length', 'fixed'])) === 0;
}
if ($columnDiff->hasChanged('type')) {
return false;
}
return count(array_diff($columnDiff->changedProperties, ['length', 'fixed'])) === 0;
}
/**
* {@inheritdoc}
*/
protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
{
if (strpos($tableName, '.') !== false) {
[$schema] = explode('.', $tableName);
$oldIndexName = $schema . '.' . $oldIndexName;
}
return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)];
}
/**
* {@inheritdoc}
*/
public function getCommentOnColumnSQL($tableName, $columnName, $comment)
{
$tableName = new Identifier($tableName);
$columnName = new Identifier($columnName);
$comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment);
return sprintf(
'COMMENT ON COLUMN %s.%s IS %s',
$tableName->getQuotedName($this),
$columnName->getQuotedName($this),
$comment
);
}
/**
* {@inheritDoc}
*/
public function getCreateSequenceSQL(Sequence $sequence)
{
return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
' INCREMENT BY ' . $sequence->getAllocationSize() .
' MINVALUE ' . $sequence->getInitialValue() .
' START ' . $sequence->getInitialValue() .
$this->getSequenceCacheSQL($sequence);
}
/**
* {@inheritDoc}
*/
public function getAlterSequenceSQL(Sequence $sequence)
{
return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
' INCREMENT BY ' . $sequence->getAllocationSize() .
$this->getSequenceCacheSQL($sequence);
}
/**
* Cache definition for sequences
*
* @return string
*/
private function getSequenceCacheSQL(Sequence $sequence)
{
if ($sequence->getCache() > 1) {
return ' CACHE ' . $sequence->getCache();
}
return '';
}
/**
* {@inheritDoc}
*/
public function getDropSequenceSQL($sequence)
{
if ($sequence instanceof Sequence) {
$sequence = $sequence->getQuotedName($this);
}
return 'DROP SEQUENCE ' . $sequence . ' CASCADE';
}
/**
* {@inheritDoc}
*/
public function getCreateSchemaSQL($schemaName)
{
return 'CREATE SCHEMA ' . $schemaName;
}
/**
* {@inheritDoc}
*/
public function getDropForeignKeySQL($foreignKey, $table)
{
return $this->getDropConstraintSQL($foreignKey, $table);
}
/**
* {@inheritDoc}
*/
protected function _getCreateTableSQL($name, array $columns, array $options = [])
{
$queryFields = $this->getColumnDeclarationListSQL($columns);
if (isset($options['primary']) && ! empty($options['primary'])) {
$keyColumns = array_unique(array_values($options['primary']));
$queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
}
$query = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')';
$sql = [$query];
if (isset($options['indexes']) && ! empty($options['indexes'])) {
foreach ($options['indexes'] as $index) {
$sql[] = $this->getCreateIndexSQL($index, $name);
}
}
if (isset($options['foreignKeys'])) {
foreach ((array) $options['foreignKeys'] as $definition) {
$sql[] = $this->getCreateForeignKeySQL($definition, $name);
}
}
return $sql;
}
/**
* Converts a single boolean value.
*
* First converts the value to its native PHP boolean type
* and passes it to the given callback function to be reconverted
* into any custom representation.
*
* @param mixed $value The value to convert.
* @param callable $callback The callback function to use for converting the real boolean value.
*
* @return mixed
*
* @throws UnexpectedValueException
*/
private function convertSingleBooleanValue($value, $callback)
{
if ($value === null) {
return $callback(null);
}
if (is_bool($value) || is_numeric($value)) {
return $callback((bool) $value);
}
if (! is_string($value)) {
return $callback(true);
}
/**
* Better safe than sorry: http://php.net/in_array#106319
*/
if (in_array(strtolower(trim($value)), $this->booleanLiterals['false'], true)) {
return $callback(false);
}
if (in_array(strtolower(trim($value)), $this->booleanLiterals['true'], true)) {
return $callback(true);
}
throw new UnexpectedValueException("Unrecognized boolean literal '${value}'");
}
/**
* Converts one or multiple boolean values.
*
* First converts the value(s) to their native PHP boolean type
* and passes them to the given callback function to be reconverted
* into any custom representation.
*
* @param mixed $item The value(s) to convert.
* @param callable $callback The callback function to use for converting the real boolean value(s).
*
* @return mixed
*/
private function doConvertBooleans($item, $callback)
{
if (is_array($item)) {
foreach ($item as $key => $value) {
$item[$key] = $this->convertSingleBooleanValue($value, $callback);
}
return $item;
}
return $this->convertSingleBooleanValue($item, $callback);
}
/**
* {@inheritDoc}
*
* Postgres wants boolean values converted to the strings 'true'/'false'.
*/
public function convertBooleans($item)
{
if (! $this->useBooleanTrueFalseStrings) {
return parent::convertBooleans($item);
}
return $this->doConvertBooleans(
$item,
static function ($boolean) {
if ($boolean === null) {
return 'NULL';
}
return $boolean === true ? 'true' : 'false';
}
);
}
/**
* {@inheritDoc}
*/
public function convertBooleansToDatabaseValue($item)
{
if (! $this->useBooleanTrueFalseStrings) {
return parent::convertBooleansToDatabaseValue($item);
}
return $this->doConvertBooleans(
$item,
static function ($boolean) {
return $boolean === null ? null : (int) $boolean;
}
);
}
/**
* {@inheritDoc}
*/
public function convertFromBoolean($item)
{
if (in_array(strtolower($item), $this->booleanLiterals['false'], true)) {
return false;
}
return parent::convertFromBoolean($item);
}
/**
* {@inheritDoc}
*/
public function getSequenceNextValSQL($sequence)
{
return "SELECT NEXTVAL('" . $sequence . "')";
}
/**
* {@inheritDoc}
*/
public function getSetTransactionIsolationSQL($level)
{
return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL '
. $this->_getTransactionIsolationLevelSQL($level);
}
/**
* {@inheritDoc}
*/
public function getBooleanTypeDeclarationSQL(array $column)
{
return 'BOOLEAN';
}
/**
* {@inheritDoc}
*/
public function getIntegerTypeDeclarationSQL(array $column)
{
if (! empty($column['autoincrement'])) {
return 'SERIAL';
}
return 'INT';
}
/**
* {@inheritDoc}
*/
public function getBigIntTypeDeclarationSQL(array $column)
{
if (! empty($column['autoincrement'])) {
return 'BIGSERIAL';
}
return 'BIGINT';
}
/**
* {@inheritDoc}
*/
public function getSmallIntTypeDeclarationSQL(array $column)
{
return 'SMALLINT';
}
/**
* {@inheritDoc}
*/
public function getGuidTypeDeclarationSQL(array $column)
{
return 'UUID';
}
/**
* {@inheritDoc}
*/
public function getDateTimeTypeDeclarationSQL(array $column)
{
return 'TIMESTAMP(0) WITHOUT TIME ZONE';
}
/**
* {@inheritDoc}
*/
public function getDateTimeTzTypeDeclarationSQL(array $column)
{
return 'TIMESTAMP(0) WITH TIME ZONE';
}
/**
* {@inheritDoc}
*/
public function getDateTypeDeclarationSQL(array $column)
{
return 'DATE';
}
/**
* {@inheritDoc}
*/
public function getTimeTypeDeclarationSQL(array $column)
{
return 'TIME(0) WITHOUT TIME ZONE';
}
/**
* {@inheritDoc}
*
* @deprecated Use application-generated UUIDs instead
*/
public function getGuidExpression()
{
return 'UUID_GENERATE_V4()';
}
/**
* {@inheritDoc}
*/
protected function _getCommonIntegerTypeDeclarationSQL(array $column)
{
return '';
}
/**
* {@inheritDoc}
*/
protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
{
return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
: ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
}
/**
* {@inheritdoc}
*/
protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
{
return 'BYTEA';
}
/**
* {@inheritDoc}
*/
public function getClobTypeDeclarationSQL(array $column)
{
return 'TEXT';
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'postgresql';
}
/**
* {@inheritDoc}
*
* PostgreSQL returns all column names in SQL result sets in lowercase.
*/
public function getSQLResultCasing($column)
{
return strtolower($column);
}
/**
* {@inheritDoc}
*/
public function getDateTimeTzFormatString()
{
return 'Y-m-d H:i:sO';
}
/**
* {@inheritDoc}
*/
public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName)
{
return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)';
}
/**
* {@inheritDoc}
*/
public function getTruncateTableSQL($tableName, $cascade = false)
{
$tableIdentifier = new Identifier($tableName);
$sql = 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
if ($cascade) {
$sql .= ' CASCADE';
}
return $sql;
}
/**
* {@inheritDoc}
*/
public function getReadLockSQL()
{
return 'FOR SHARE';
}
/**
* {@inheritDoc}
*/
protected function initializeDoctrineTypeMappings()
{
$this->doctrineTypeMapping = [
'smallint' => 'smallint',
'int2' => 'smallint',
'serial' => 'integer',
'serial4' => 'integer',
'int' => 'integer',
'int4' => 'integer',
'integer' => 'integer',
'bigserial' => 'bigint',
'serial8' => 'bigint',
'bigint' => 'bigint',
'int8' => 'bigint',
'bool' => 'boolean',
'boolean' => 'boolean',
'text' => 'text',
'tsvector' => 'text',
'varchar' => 'string',
'interval' => 'string',
'_varchar' => 'string',
'char' => 'string',
'bpchar' => 'string',
'inet' => 'string',
'date' => 'date',
'datetime' => 'datetime',
'timestamp' => 'datetime',
'timestamptz' => 'datetimetz',
'time' => 'time',
'timetz' => 'time',
'float' => 'float',
'float4' => 'float',
'float8' => 'float',
'double' => 'float',
'double precision' => 'float',
'real' => 'float',
'decimal' => 'decimal',
'money' => 'decimal',
'numeric' => 'decimal',
'year' => 'date',
'uuid' => 'guid',
'bytea' => 'blob',
];
}
/**
* {@inheritDoc}
*/
public function getVarcharMaxLength()
{
return 65535;
}
/**
* {@inheritdoc}
*/
public function getBinaryMaxLength()
{
return 0;
}
/**
* {@inheritdoc}
*/
public function getBinaryDefaultLength()
{
return 0;
}
/**
* {@inheritDoc}
*/
protected function getReservedKeywordsClass()
{
return Keywords\PostgreSQLKeywords::class;
}
/**
* {@inheritDoc}
*/
public function getBlobTypeDeclarationSQL(array $column)
{
return 'BYTEA';
}
/**
* {@inheritdoc}
*/
public function getDefaultValueDeclarationSQL($column)
{
if ($this->isSerialColumn($column)) {
return '';
}
return parent::getDefaultValueDeclarationSQL($column);
}
/**
* @param mixed[] $column
*/
private function isSerialColumn(array $column): bool
{
return isset($column['type'], $column['autoincrement'])
&& $column['autoincrement'] === true
&& $this->isNumericType($column['type']);
}
/**
* Check whether the type of a column is changed in a way that invalidates the default value for the column
*/
private function typeChangeBreaksDefaultValue(ColumnDiff $columnDiff): bool
{
if (! $columnDiff->fromColumn) {
return $columnDiff->hasChanged('type');
}
$oldTypeIsNumeric = $this->isNumericType($columnDiff->fromColumn->getType());
$newTypeIsNumeric = $this->isNumericType($columnDiff->column->getType());
// default should not be changed when switching between numeric types and the default comes from a sequence
return $columnDiff->hasChanged('type')
&& ! ($oldTypeIsNumeric && $newTypeIsNumeric && $columnDiff->column->getAutoincrement());
}
private function isNumericType(Type $type): bool
{
return $type instanceof IntegerType || $type instanceof BigIntType;
}
private function getOldColumnComment(ColumnDiff $columnDiff): ?string
{
return $columnDiff->fromColumn ? $this->getColumnComment($columnDiff->fromColumn) : null;
}
public function getListTableMetadataSQL(string $table, ?string $schema = null): string
{
if ($schema !== null) {
$table = $schema . '.' . $table;
}
return sprintf(
<<<'SQL'
SELECT obj_description(%s::regclass) AS table_comment;
SQL
,
$this->quoteStringLiteral($table)
);
}
}
lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php 0000644 00000001002 13727250467 0016262 0 ustar 00 getQuotedName($this) .
' INCREMENT BY ' . $sequence->getAllocationSize() .
' START WITH ' . $sequence->getInitialValue() .
' MINVALUE ' . $sequence->getInitialValue();
}
/**
* {@inheritdoc}
*/
public function getAlterSequenceSQL(Sequence $sequence)
{
return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
' INCREMENT BY ' . $sequence->getAllocationSize();
}
/**
* {@inheritdoc}
*/
public function getDateTimeTzFormatString()
{
return 'Y-m-d H:i:s.uP';
}
/**
* {@inheritdoc}
*/
public function getDateTimeTzTypeDeclarationSQL(array $column)
{
return 'TIMESTAMP WITH TIME ZONE';
}
/**
* {@inheritdoc}
*/
public function getDropSequenceSQL($sequence)
{
if ($sequence instanceof Sequence) {
$sequence = $sequence->getQuotedName($this);
}
return 'DROP SEQUENCE ' . $sequence;
}
/**
* {@inheritdoc}
*/
public function getListSequencesSQL($database)
{
return 'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE';
}
/**
* {@inheritdoc}
*/
public function getSequenceNextValSQL($sequence)
{
return 'SELECT ' . $sequence . '.NEXTVAL';
}
/**
* {@inheritdoc}
*/
public function supportsSequences()
{
return true;
}
/**
* {@inheritdoc}
*/
protected function getAdvancedIndexOptionsSQL(Index $index)
{
if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_not_distinct')) {
return ' WITH NULLS NOT DISTINCT' . parent::getAdvancedIndexOptionsSQL($index);
}
return parent::getAdvancedIndexOptionsSQL($index);
}
/**
* {@inheritdoc}
*/
protected function getReservedKeywordsClass()
{
return Keywords\SQLAnywhere12Keywords::class;
}
/**
* {@inheritDoc}
*/
protected function initializeDoctrineTypeMappings()
{
parent::initializeDoctrineTypeMappings();
$this->doctrineTypeMapping['timestamp with time zone'] = 'datetime';
}
}
lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php 0000644 00000002144 13727250467 0016277 0 ustar 00 hasFlag('with_nulls_distinct') && $index->hasFlag('with_nulls_not_distinct')) {
throw new UnexpectedValueException(
'An Index can either have a "with_nulls_distinct" or "with_nulls_not_distinct" flag but not both.'
);
}
if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_distinct')) {
return ' WITH NULLS DISTINCT' . parent::getAdvancedIndexOptionsSQL($index);
}
return parent::getAdvancedIndexOptionsSQL($index);
}
/**
* {@inheritdoc}
*/
protected function getReservedKeywordsClass()
{
return Keywords\SQLAnywhere16Keywords::class;
}
}
lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php 0000644 00000123610 13727250467 0016132 0 ustar 00 getMaxIdentifierLength();
if (strlen($schemaElementName) > $maxIdentifierLength) {
return substr($schemaElementName, 0, $maxIdentifierLength);
}
return $schemaElementName;
}
/**
* {@inheritdoc}
*/
public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
{
$query = '';
if ($foreignKey->hasOption('match')) {
$query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match'));
}
$query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
if ($foreignKey->hasOption('check_on_commit') && (bool) $foreignKey->getOption('check_on_commit')) {
$query .= ' CHECK ON COMMIT';
}
if ($foreignKey->hasOption('clustered') && (bool) $foreignKey->getOption('clustered')) {
$query .= ' CLUSTERED';
}
if ($foreignKey->hasOption('for_olap_workload') && (bool) $foreignKey->getOption('for_olap_workload')) {
$query .= ' FOR OLAP WORKLOAD';
}
return $query;
}
/**
* {@inheritdoc}
*/
public function getAlterTableSQL(TableDiff $diff)
{
$sql = [];
$columnSql = [];
$commentsSQL = [];
$tableSql = [];
$alterClauses = [];
foreach ($diff->addedColumns as $column) {
if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
continue;
}
$alterClauses[] = $this->getAlterTableAddColumnClause($column);
$comment = $this->getColumnComment($column);
if ($comment === null || $comment === '') {
continue;
}
$commentsSQL[] = $this->getCommentOnColumnSQL(
$diff->getName($this)->getQuotedName($this),
$column->getQuotedName($this),
$comment
);
}
foreach ($diff->removedColumns as $column) {
if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
continue;
}
$alterClauses[] = $this->getAlterTableRemoveColumnClause($column);
}
foreach ($diff->changedColumns as $columnDiff) {
if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
continue;
}
$alterClause = $this->getAlterTableChangeColumnClause($columnDiff);
if ($alterClause !== null) {
$alterClauses[] = $alterClause;
}
if (! $columnDiff->hasChanged('comment')) {
continue;
}
$column = $columnDiff->column;
$commentsSQL[] = $this->getCommentOnColumnSQL(
$diff->getName($this)->getQuotedName($this),
$column->getQuotedName($this),
$this->getColumnComment($column)
);
}
foreach ($diff->renamedColumns as $oldColumnName => $column) {
if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
continue;
}
$sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
$this->getAlterTableRenameColumnClause($oldColumnName, $column);
}
if (! $this->onSchemaAlterTable($diff, $tableSql)) {
if (! empty($alterClauses)) {
$sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(', ', $alterClauses);
}
$sql = array_merge($sql, $commentsSQL);
$newName = $diff->getNewName();
if ($newName !== false) {
$sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
$this->getAlterTableRenameTableClause($newName);
}
$sql = array_merge(
$this->getPreAlterTableIndexForeignKeySQL($diff),
$sql,
$this->getPostAlterTableIndexForeignKeySQL($diff)
);
}
return array_merge($sql, $tableSql, $columnSql);
}
/**
* Returns the SQL clause for creating a column in a table alteration.
*
* @param Column $column The column to add.
*
* @return string
*/
protected function getAlterTableAddColumnClause(Column $column)
{
return 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
}
/**
* Returns the SQL clause for altering a table.
*
* @param Identifier $tableName The quoted name of the table to alter.
*
* @return string
*/
protected function getAlterTableClause(Identifier $tableName)
{
return 'ALTER TABLE ' . $tableName->getQuotedName($this);
}
/**
* Returns the SQL clause for dropping a column in a table alteration.
*
* @param Column $column The column to drop.
*
* @return string
*/
protected function getAlterTableRemoveColumnClause(Column $column)
{
return 'DROP ' . $column->getQuotedName($this);
}
/**
* Returns the SQL clause for renaming a column in a table alteration.
*
* @param string $oldColumnName The quoted name of the column to rename.
* @param Column $column The column to rename to.
*
* @return string
*/
protected function getAlterTableRenameColumnClause($oldColumnName, Column $column)
{
$oldColumnName = new Identifier($oldColumnName);
return 'RENAME ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this);
}
/**
* Returns the SQL clause for renaming a table in a table alteration.
*
* @param Identifier $newTableName The quoted name of the table to rename to.
*
* @return string
*/
protected function getAlterTableRenameTableClause(Identifier $newTableName)
{
return 'RENAME ' . $newTableName->getQuotedName($this);
}
/**
* Returns the SQL clause for altering a column in a table alteration.
*
* This method returns null in case that only the column comment has changed.
* Changes in column comments have to be handled differently.
*
* @param ColumnDiff $columnDiff The diff of the column to alter.
*
* @return string|null
*/
protected function getAlterTableChangeColumnClause(ColumnDiff $columnDiff)
{
$column = $columnDiff->column;
// Do not return alter clause if only comment has changed.
if (! ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1)) {
$columnAlterationClause = 'ALTER ' .
$this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
if ($columnDiff->hasChanged('default') && $column->getDefault() === null) {
$columnAlterationClause .= ', ALTER ' . $column->getQuotedName($this) . ' DROP DEFAULT';
}
return $columnAlterationClause;
}
return null;
}
/**
* {@inheritdoc}
*/
public function getBigIntTypeDeclarationSQL(array $column)
{
$column['integer_type'] = 'BIGINT';
return $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritdoc}
*/
public function getBinaryDefaultLength()
{
return 1;
}
/**
* {@inheritdoc}
*/
public function getBinaryMaxLength()
{
return 32767;
}
/**
* {@inheritdoc}
*/
public function getBlobTypeDeclarationSQL(array $column)
{
return 'LONG BINARY';
}
/**
* {@inheritdoc}
*
* BIT type columns require an explicit NULL declaration
* in SQL Anywhere if they shall be nullable.
* Otherwise by just omitting the NOT NULL clause,
* SQL Anywhere will declare them NOT NULL nonetheless.
*/
public function getBooleanTypeDeclarationSQL(array $column)
{
$nullClause = isset($column['notnull']) && (bool) $column['notnull'] === false ? ' NULL' : '';
return 'BIT' . $nullClause;
}
/**
* {@inheritdoc}
*/
public function getClobTypeDeclarationSQL(array $column)
{
return 'TEXT';
}
/**
* {@inheritdoc}
*/
public function getCommentOnColumnSQL($tableName, $columnName, $comment)
{
$tableName = new Identifier($tableName);
$columnName = new Identifier($columnName);
$comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment);
return sprintf(
'COMMENT ON COLUMN %s.%s IS %s',
$tableName->getQuotedName($this),
$columnName->getQuotedName($this),
$comment
);
}
/**
* {@inheritdoc}
*/
public function getConcatExpression()
{
return 'STRING(' . implode(', ', (array) func_get_args()) . ')';
}
/**
* {@inheritdoc}
*/
public function getCreateConstraintSQL(Constraint $constraint, $table)
{
if ($constraint instanceof ForeignKeyConstraint) {
return $this->getCreateForeignKeySQL($constraint, $table);
}
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
}
return 'ALTER TABLE ' . $table .
' ADD ' . $this->getTableConstraintDeclarationSQL($constraint, $constraint->getQuotedName($this));
}
/**
* {@inheritdoc}
*/
public function getCreateDatabaseSQL($database)
{
$database = new Identifier($database);
return "CREATE DATABASE '" . $database->getName() . "'";
}
/**
* {@inheritdoc}
*
* Appends SQL Anywhere specific flags if given.
*/
public function getCreateIndexSQL(Index $index, $table)
{
return parent::getCreateIndexSQL($index, $table) . $this->getAdvancedIndexOptionsSQL($index);
}
/**
* {@inheritdoc}
*/
public function getCreatePrimaryKeySQL(Index $index, $table)
{
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
}
return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index);
}
/**
* {@inheritdoc}
*/
public function getCreateTemporaryTableSnippetSQL()
{
return 'CREATE ' . $this->getTemporaryTableSQL() . ' TABLE';
}
/**
* {@inheritdoc}
*/
public function getCreateViewSQL($name, $sql)
{
return 'CREATE VIEW ' . $name . ' AS ' . $sql;
}
/**
* {@inheritdoc}
*/
public function getCurrentDateSQL()
{
return 'CURRENT DATE';
}
/**
* {@inheritdoc}
*/
public function getCurrentTimeSQL()
{
return 'CURRENT TIME';
}
/**
* {@inheritdoc}
*/
public function getCurrentTimestampSQL()
{
return 'CURRENT TIMESTAMP';
}
/**
* {@inheritdoc}
*/
protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
{
$factorClause = '';
if ($operator === '-') {
$factorClause = '-1 * ';
}
return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')';
}
/**
* {@inheritdoc}
*/
public function getDateDiffExpression($date1, $date2)
{
return 'DATEDIFF(day, ' . $date2 . ', ' . $date1 . ')';
}
/**
* {@inheritdoc}
*/
public function getDateTimeFormatString()
{
return 'Y-m-d H:i:s.u';
}
/**
* {@inheritdoc}
*/
public function getDateTimeTypeDeclarationSQL(array $column)
{
return 'DATETIME';
}
/**
* {@inheritdoc}
*/
public function getDateTimeTzFormatString()
{
return $this->getDateTimeFormatString();
}
/**
* {@inheritdoc}
*/
public function getDateTypeDeclarationSQL(array $column)
{
return 'DATE';
}
/**
* {@inheritdoc}
*/
public function getDefaultTransactionIsolationLevel()
{
return TransactionIsolationLevel::READ_UNCOMMITTED;
}
/**
* {@inheritdoc}
*/
public function getDropDatabaseSQL($database)
{
$database = new Identifier($database);
return "DROP DATABASE '" . $database->getName() . "'";
}
/**
* {@inheritdoc}
*/
public function getDropIndexSQL($index, $table = null)
{
if ($index instanceof Index) {
$index = $index->getQuotedName($this);
}
if (! is_string($index)) {
throw new InvalidArgumentException(
__METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.'
);
}
if (! isset($table)) {
return 'DROP INDEX ' . $index;
}
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
}
if (! is_string($table)) {
throw new InvalidArgumentException(
__METHOD__ . '() expects $table parameter to be string or ' . Index::class . '.'
);
}
return 'DROP INDEX ' . $table . '.' . $index;
}
/**
* {@inheritdoc}
*/
public function getDropViewSQL($name)
{
return 'DROP VIEW ' . $name;
}
/**
* {@inheritdoc}
*/
public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
{
$sql = '';
$foreignKeyName = $foreignKey->getName();
$localColumns = $foreignKey->getQuotedLocalColumns($this);
$foreignColumns = $foreignKey->getQuotedForeignColumns($this);
$foreignTableName = $foreignKey->getQuotedForeignTableName($this);
if (! empty($foreignKeyName)) {
$sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
}
if (empty($localColumns)) {
throw new InvalidArgumentException("Incomplete definition. 'local' required.");
}
if (empty($foreignColumns)) {
throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
}
if (empty($foreignTableName)) {
throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
}
if ($foreignKey->hasOption('notnull') && (bool) $foreignKey->getOption('notnull')) {
$sql .= 'NOT NULL ';
}
return $sql .
'FOREIGN KEY (' . $this->getIndexFieldDeclarationListSQL($localColumns) . ') ' .
'REFERENCES ' . $foreignKey->getQuotedForeignTableName($this) .
' (' . $this->getIndexFieldDeclarationListSQL($foreignColumns) . ')';
}
/**
* Returns foreign key MATCH clause for given type.
*
* @param int $type The foreign key match type
*
* @return string
*
* @throws InvalidArgumentException If unknown match type given.
*/
public function getForeignKeyMatchClauseSQL($type)
{
switch ((int) $type) {
case self::FOREIGN_KEY_MATCH_SIMPLE:
return 'SIMPLE';
case self::FOREIGN_KEY_MATCH_FULL:
return 'FULL';
case self::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE:
return 'UNIQUE SIMPLE';
case self::FOREIGN_KEY_MATCH_FULL_UNIQUE:
return 'UNIQUE FULL';
default:
throw new InvalidArgumentException('Invalid foreign key match type: ' . $type);
}
}
/**
* {@inheritdoc}
*/
public function getForeignKeyReferentialActionSQL($action)
{
// NO ACTION is not supported, therefore falling back to RESTRICT.
if (strtoupper($action) === 'NO ACTION') {
return 'RESTRICT';
}
return parent::getForeignKeyReferentialActionSQL($action);
}
/**
* {@inheritdoc}
*/
public function getForUpdateSQL()
{
return '';
}
/**
* {@inheritdoc}
*
* @deprecated Use application-generated UUIDs instead
*/
public function getGuidExpression()
{
return 'NEWID()';
}
/**
* {@inheritdoc}
*/
public function getGuidTypeDeclarationSQL(array $column)
{
return 'UNIQUEIDENTIFIER';
}
/**
* {@inheritdoc}
*/
public function getIndexDeclarationSQL($name, Index $index)
{
// Index declaration in statements like CREATE TABLE is not supported.
throw DBALException::notSupported(__METHOD__);
}
/**
* {@inheritdoc}
*/
public function getIntegerTypeDeclarationSQL(array $column)
{
$column['integer_type'] = 'INT';
return $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* {@inheritdoc}
*/
public function getListDatabasesSQL()
{
return 'SELECT db_name(number) AS name FROM sa_db_list()';
}
/**
* {@inheritdoc}
*/
public function getListTableColumnsSQL($table, $database = null)
{
$user = 'USER_NAME()';
if (strpos($table, '.') !== false) {
[$user, $table] = explode('.', $table);
$user = $this->quoteStringLiteral($user);
}
return sprintf(
<<<'SQL'
SELECT col.column_name,
COALESCE(def.user_type_name, def.domain_name) AS 'type',
def.declared_width AS 'length',
def.scale,
CHARINDEX('unsigned', def.domain_name) AS 'unsigned',
IF col.nulls = 'Y' THEN 0 ELSE 1 ENDIF AS 'notnull',
col."default",
def.is_autoincrement AS 'autoincrement',
rem.remarks AS 'comment'
FROM sa_describe_query('SELECT * FROM "%s"') AS def
JOIN SYS.SYSTABCOL AS col
ON col.table_id = def.base_table_id AND col.column_id = def.base_column_id
LEFT JOIN SYS.SYSREMARK AS rem
ON col.object_id = rem.object_id
WHERE def.base_owner_name = %s
ORDER BY def.base_column_id ASC
SQL
,
$table,
$user
);
}
/**
* {@inheritdoc}
*
* @todo Where is this used? Which information should be retrieved?
*/
public function getListTableConstraintsSQL($table)
{
$user = '';
if (strpos($table, '.') !== false) {
[$user, $table] = explode('.', $table);
$user = $this->quoteStringLiteral($user);
$table = $this->quoteStringLiteral($table);
} else {
$table = $this->quoteStringLiteral($table);
}
return sprintf(
<<<'SQL'
SELECT con.*
FROM SYS.SYSCONSTRAINT AS con
JOIN SYS.SYSTAB AS tab ON con.table_object_id = tab.object_id
WHERE tab.table_name = %s
AND tab.creator = USER_ID(%s)
SQL
,
$table,
$user
);
}
/**
* {@inheritdoc}
*/
public function getListTableForeignKeysSQL($table)
{
$user = '';
if (strpos($table, '.') !== false) {
[$user, $table] = explode('.', $table);
$user = $this->quoteStringLiteral($user);
$table = $this->quoteStringLiteral($table);
} else {
$table = $this->quoteStringLiteral($table);
}
return sprintf(
<<<'SQL'
SELECT fcol.column_name AS local_column,
ptbl.table_name AS foreign_table,
pcol.column_name AS foreign_column,
idx.index_name,
IF fk.nulls = 'N'
THEN 1
ELSE NULL
ENDIF AS notnull,
CASE ut.referential_action
WHEN 'C' THEN 'CASCADE'
WHEN 'D' THEN 'SET DEFAULT'
WHEN 'N' THEN 'SET NULL'
WHEN 'R' THEN 'RESTRICT'
ELSE NULL
END AS on_update,
CASE dt.referential_action
WHEN 'C' THEN 'CASCADE'
WHEN 'D' THEN 'SET DEFAULT'
WHEN 'N' THEN 'SET NULL'
WHEN 'R' THEN 'RESTRICT'
ELSE NULL
END AS on_delete,
IF fk.check_on_commit = 'Y'
THEN 1
ELSE NULL
ENDIF AS check_on_commit, -- check_on_commit flag
IF ftbl.clustered_index_id = idx.index_id
THEN 1
ELSE NULL
ENDIF AS 'clustered', -- clustered flag
IF fk.match_type = 0
THEN NULL
ELSE fk.match_type
ENDIF AS 'match', -- match option
IF pidx.max_key_distance = 1
THEN 1
ELSE NULL
ENDIF AS for_olap_workload -- for_olap_workload flag
FROM SYS.SYSFKEY AS fk
JOIN SYS.SYSIDX AS idx
ON fk.foreign_table_id = idx.table_id
AND fk.foreign_index_id = idx.index_id
JOIN SYS.SYSPHYSIDX pidx
ON idx.table_id = pidx.table_id
AND idx.phys_index_id = pidx.phys_index_id
JOIN SYS.SYSTAB AS ptbl
ON fk.primary_table_id = ptbl.table_id
JOIN SYS.SYSTAB AS ftbl
ON fk.foreign_table_id = ftbl.table_id
JOIN SYS.SYSIDXCOL AS idxcol
ON idx.table_id = idxcol.table_id
AND idx.index_id = idxcol.index_id
JOIN SYS.SYSTABCOL AS pcol
ON ptbl.table_id = pcol.table_id
AND idxcol.primary_column_id = pcol.column_id
JOIN SYS.SYSTABCOL AS fcol
ON ftbl.table_id = fcol.table_id
AND idxcol.column_id = fcol.column_id
LEFT JOIN SYS.SYSTRIGGER ut
ON fk.foreign_table_id = ut.foreign_table_id
AND fk.foreign_index_id = ut.foreign_key_id
AND ut.event = 'C'
LEFT JOIN SYS.SYSTRIGGER dt
ON fk.foreign_table_id = dt.foreign_table_id
AND fk.foreign_index_id = dt.foreign_key_id
AND dt.event = 'D'
WHERE ftbl.table_name = %s
AND ftbl.creator = USER_ID(%s)
ORDER BY fk.foreign_index_id ASC, idxcol.sequence ASC
SQL
,
$table,
$user
);
}
/**
* {@inheritdoc}
*/
public function getListTableIndexesSQL($table, $database = null)
{
$user = '';
if (strpos($table, '.') !== false) {
[$user, $table] = explode('.', $table);
$user = $this->quoteStringLiteral($user);
$table = $this->quoteStringLiteral($table);
} else {
$table = $this->quoteStringLiteral($table);
}
return sprintf(
<<<'SQL'
SELECT idx.index_name AS key_name,
IF idx.index_category = 1
THEN 1
ELSE 0
ENDIF AS 'primary',
col.column_name,
IF idx."unique" IN(1, 2, 5)
THEN 0
ELSE 1
ENDIF AS non_unique,
IF tbl.clustered_index_id = idx.index_id
THEN 1
ELSE NULL
ENDIF AS 'clustered', -- clustered flag
IF idx."unique" = 5
THEN 1
ELSE NULL
ENDIF AS with_nulls_not_distinct, -- with_nulls_not_distinct flag
IF pidx.max_key_distance = 1
THEN 1
ELSE NULL
ENDIF AS for_olap_workload -- for_olap_workload flag
FROM SYS.SYSIDX AS idx
JOIN SYS.SYSPHYSIDX pidx
ON idx.table_id = pidx.table_id
AND idx.phys_index_id = pidx.phys_index_id
JOIN SYS.SYSIDXCOL AS idxcol
ON idx.table_id = idxcol.table_id AND idx.index_id = idxcol.index_id
JOIN SYS.SYSTABCOL AS col
ON idxcol.table_id = col.table_id AND idxcol.column_id = col.column_id
JOIN SYS.SYSTAB AS tbl
ON idx.table_id = tbl.table_id
WHERE tbl.table_name = %s
AND tbl.creator = USER_ID(%s)
AND idx.index_category != 2 -- exclude indexes implicitly created by foreign key constraints
ORDER BY idx.index_id ASC, idxcol.sequence ASC
SQL
,
$table,
$user
);
}
/**
* {@inheritdoc}
*/
public function getListTablesSQL()
{
return "SELECT tbl.table_name
FROM SYS.SYSTAB AS tbl
JOIN SYS.SYSUSER AS usr ON tbl.creator = usr.user_id
JOIN dbo.SYSOBJECTS AS obj ON tbl.object_id = obj.id
WHERE tbl.table_type IN(1, 3) -- 'BASE', 'GBL TEMP'
AND usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users
AND obj.type = 'U' -- user created tables only
ORDER BY tbl.table_name ASC";
}
/**
* {@inheritdoc}
*
* @todo Where is this used? Which information should be retrieved?
*/
public function getListUsersSQL()
{
return 'SELECT * FROM SYS.SYSUSER ORDER BY user_name ASC';
}
/**
* {@inheritdoc}
*/
public function getListViewsSQL($database)
{
return "SELECT tbl.table_name, v.view_def
FROM SYS.SYSVIEW v
JOIN SYS.SYSTAB tbl ON v.view_object_id = tbl.object_id
JOIN SYS.SYSUSER usr ON tbl.creator = usr.user_id
JOIN dbo.SYSOBJECTS obj ON tbl.object_id = obj.id
WHERE usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users
ORDER BY tbl.table_name ASC";
}
/**
* {@inheritdoc}
*/
public function getLocateExpression($str, $substr, $startPos = false)
{
if ($startPos === false) {
return 'LOCATE(' . $str . ', ' . $substr . ')';
}
return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')';
}
/**
* {@inheritdoc}
*/
public function getMaxIdentifierLength()
{
return 128;
}
/**
* {@inheritdoc}
*/
public function getMd5Expression($column)
{
return 'HASH(' . $column . ", 'MD5')";
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'sqlanywhere';
}
/**
* Obtain DBMS specific SQL code portion needed to set a primary key
* declaration to be used in statements like ALTER TABLE.
*
* @param Index $index Index definition
* @param string $name Name of the primary key
*
* @return string DBMS specific SQL code portion needed to set a primary key
*
* @throws InvalidArgumentException If the given index is not a primary key.
*/
public function getPrimaryKeyDeclarationSQL(Index $index, $name = null)
{
if (! $index->isPrimary()) {
throw new InvalidArgumentException(
'Can only create primary key declarations with getPrimaryKeyDeclarationSQL()'
);
}
return $this->getTableConstraintDeclarationSQL($index, $name);
}
/**
* {@inheritdoc}
*/
public function getSetTransactionIsolationSQL($level)
{
return 'SET TEMPORARY OPTION isolation_level = ' . $this->_getTransactionIsolationLevelSQL($level);
}
/**
* {@inheritdoc}
*/
public function getSmallIntTypeDeclarationSQL(array $column)
{
$column['integer_type'] = 'SMALLINT';
return $this->_getCommonIntegerTypeDeclarationSQL($column);
}
/**
* Returns the SQL statement for starting an existing database.
*
* In SQL Anywhere you can start and stop databases on a
* database server instance.
* This is a required statement after having created a new database
* as it has to be explicitly started to be usable.
* SQL Anywhere does not automatically start a database after creation!
*
* @param string $database Name of the database to start.
*
* @return string
*/
public function getStartDatabaseSQL($database)
{
$database = new Identifier($database);
return "START DATABASE '" . $database->getName() . "' AUTOSTOP OFF";
}
/**
* Returns the SQL statement for stopping a running database.
*
* In SQL Anywhere you can start and stop databases on a
* database server instance.
* This is a required statement before dropping an existing database
* as it has to be explicitly stopped before it can be dropped.
*
* @param string $database Name of the database to stop.
*
* @return string
*/
public function getStopDatabaseSQL($database)
{
$database = new Identifier($database);
return 'STOP DATABASE "' . $database->getName() . '" UNCONDITIONALLY';
}
/**
* {@inheritdoc}
*/
public function getSubstringExpression($string, $start, $length = null)
{
if ($length === null) {
return 'SUBSTRING(' . $string . ', ' . $start . ')';
}
return 'SUBSTRING(' . $string . ', ' . $start . ', ' . $length . ')';
}
/**
* {@inheritdoc}
*/
public function getTemporaryTableSQL()
{
return 'GLOBAL TEMPORARY';
}
/**
* {@inheritdoc}
*/
public function getTimeFormatString()
{
return 'H:i:s.u';
}
/**
* {@inheritdoc}
*/
public function getTimeTypeDeclarationSQL(array $column)
{
return 'TIME';
}
/**
* {@inheritdoc}
*/
public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false)
{
if (! $char) {
switch ($mode) {
case TrimMode::LEADING:
return $this->getLtrimExpression($str);
case TrimMode::TRAILING:
return $this->getRtrimExpression($str);
default:
return 'TRIM(' . $str . ')';
}
}
$pattern = "'%[^' + " . $char . " + ']%'";
switch ($mode) {
case TrimMode::LEADING:
return 'SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))';
case TrimMode::TRAILING:
return 'REVERSE(SUBSTR(REVERSE(' . $str . '), PATINDEX(' . $pattern . ', REVERSE(' . $str . '))))';
default:
return 'REVERSE(SUBSTR(REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))), ' .
'PATINDEX(' . $pattern . ', ' .
'REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))))))';
}
}
/**
* {@inheritdoc}
*/
public function getTruncateTableSQL($tableName, $cascade = false)
{
$tableIdentifier = new Identifier($tableName);
return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this);
}
/**
* {@inheritdoc}
*/
public function getUniqueConstraintDeclarationSQL($name, Index $index)
{
if ($index->isPrimary()) {
throw new InvalidArgumentException(
'Cannot create primary key constraint declarations with getUniqueConstraintDeclarationSQL().'
);
}
if (! $index->isUnique()) {
throw new InvalidArgumentException(
'Can only create unique constraint declarations, no common index declarations with ' .
'getUniqueConstraintDeclarationSQL().'
);
}
return $this->getTableConstraintDeclarationSQL($index, $name);
}
/**
* {@inheritdoc}
*/
public function getVarcharDefaultLength()
{
return 1;
}
/**
* {@inheritdoc}
*/
public function getVarcharMaxLength()
{
return 32767;
}
/**
* {@inheritdoc}
*/
public function hasNativeGuidType()
{
return true;
}
/**
* {@inheritdoc}
*/
public function prefersIdentityColumns()
{
return true;
}
/**
* {@inheritdoc}
*/
public function supportsCommentOnStatement()
{
return true;
}
/**
* {@inheritdoc}
*/
public function supportsIdentityColumns()
{
return true;
}
/**
* {@inheritdoc}
*/
protected function _getCommonIntegerTypeDeclarationSQL(array $column)
{
$unsigned = ! empty($column['unsigned']) ? 'UNSIGNED ' : '';
$autoincrement = ! empty($column['autoincrement']) ? ' IDENTITY' : '';
return $unsigned . $column['integer_type'] . $autoincrement;
}
/**
* {@inheritdoc}
*/
protected function _getCreateTableSQL($name, array $columns, array $options = [])
{
$columnListSql = $this->getColumnDeclarationListSQL($columns);
$indexSql = [];
if (! empty($options['uniqueConstraints'])) {
foreach ((array) $options['uniqueConstraints'] as $name => $definition) {
$columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
}
}
if (! empty($options['indexes'])) {
foreach ((array) $options['indexes'] as $index) {
assert($index instanceof Index);
$indexSql[] = $this->getCreateIndexSQL($index, $name);
}
}
if (! empty($options['primary'])) {
$flags = '';
if (isset($options['primary_index']) && $options['primary_index']->hasFlag('clustered')) {
$flags = ' CLUSTERED ';
}
$columnListSql .= ', PRIMARY KEY' . $flags
. ' (' . implode(', ', array_unique(array_values((array) $options['primary']))) . ')';
}
if (! empty($options['foreignKeys'])) {
foreach ((array) $options['foreignKeys'] as $definition) {
$columnListSql .= ', ' . $this->getForeignKeyDeclarationSQL($definition);
}
}
$query = 'CREATE TABLE ' . $name . ' (' . $columnListSql;
$check = $this->getCheckDeclarationSQL($columns);
if (! empty($check)) {
$query .= ', ' . $check;
}
$query .= ')';
return array_merge([$query], $indexSql);
}
/**
* {@inheritdoc}
*/
protected function _getTransactionIsolationLevelSQL($level)
{
switch ($level) {
case TransactionIsolationLevel::READ_UNCOMMITTED:
return '0';
case TransactionIsolationLevel::READ_COMMITTED:
return '1';
case TransactionIsolationLevel::REPEATABLE_READ:
return '2';
case TransactionIsolationLevel::SERIALIZABLE:
return '3';
default:
throw new InvalidArgumentException('Invalid isolation level:' . $level);
}
}
/**
* {@inheritdoc}
*/
protected function doModifyLimitQuery($query, $limit, $offset)
{
$limitOffsetClause = $this->getTopClauseSQL($limit, $offset);
if ($limitOffsetClause === '') {
return $query;
}
if (! preg_match('/^\s*(SELECT\s+(DISTINCT\s+)?)(.*)/i', $query, $matches)) {
return $query;
}
return $matches[1] . $limitOffsetClause . ' ' . $matches[3];
}
private function getTopClauseSQL(?int $limit, ?int $offset): string
{
if ($offset > 0) {
return sprintf('TOP %s START AT %d', $limit ?? 'ALL', $offset + 1);
}
return $limit === null ? '' : 'TOP ' . $limit;
}
/**
* Return the INDEX query section dealing with non-standard
* SQL Anywhere options.
*
* @param Index $index Index definition
*
* @return string
*/
protected function getAdvancedIndexOptionsSQL(Index $index)
{
$sql = '';
if (! $index->isPrimary() && $index->hasFlag('for_olap_workload')) {
$sql .= ' FOR OLAP WORKLOAD';
}
return $sql;
}
/**
* {@inheritdoc}
*/
protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
{
return $fixed
? 'BINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')'
: 'VARBINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')';
}
/**
* Returns the SQL snippet for creating a table constraint.
*
* @param Constraint $constraint The table constraint to create the SQL snippet for.
* @param string|null $name The table constraint name to use if any.
*
* @return string
*
* @throws InvalidArgumentException If the given table constraint type is not supported by this method.
*/
protected function getTableConstraintDeclarationSQL(Constraint $constraint, $name = null)
{
if ($constraint instanceof ForeignKeyConstraint) {
return $this->getForeignKeyDeclarationSQL($constraint);
}
if (! $constraint instanceof Index) {
throw new InvalidArgumentException('Unsupported constraint type: ' . get_class($constraint));
}
if (! $constraint->isPrimary() && ! $constraint->isUnique()) {
throw new InvalidArgumentException(
'Can only create primary, unique or foreign key constraint declarations, no common index declarations'
. ' with getTableConstraintDeclarationSQL().'
);
}
$constraintColumns = $constraint->getQuotedColumns($this);
if (empty($constraintColumns)) {
throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
}
$sql = '';
$flags = '';
if (! empty($name)) {
$name = new Identifier($name);
$sql .= 'CONSTRAINT ' . $name->getQuotedName($this) . ' ';
}
if ($constraint->hasFlag('clustered')) {
$flags = 'CLUSTERED ';
}
if ($constraint->isPrimary()) {
return $sql . 'PRIMARY KEY ' . $flags
. '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
}
return $sql . 'UNIQUE ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
}
/**
* {@inheritdoc}
*/
protected function getCreateIndexSQLFlags(Index $index)
{
$type = '';
if ($index->hasFlag('virtual')) {
$type .= 'VIRTUAL ';
}
if ($index->isUnique()) {
$type .= 'UNIQUE ';
}
if ($index->hasFlag('clustered')) {
$type .= 'CLUSTERED ';
}
return $type;
}
/**
* {@inheritdoc}
*/
protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
{
return ['ALTER INDEX ' . $oldIndexName . ' ON ' . $tableName . ' RENAME TO ' . $index->getQuotedName($this)];
}
/**
* {@inheritdoc}
*/
protected function getReservedKeywordsClass()
{
return Keywords\SQLAnywhereKeywords::class;
}
/**
* {@inheritdoc}
*/
protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
{
return $fixed
? ($length ? 'CHAR(' . $length . ')' : 'CHAR(' . $this->getVarcharDefaultLength() . ')')
: ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(' . $this->getVarcharDefaultLength() . ')');
}
/**
* {@inheritdoc}
*/
protected function initializeDoctrineTypeMappings()
{
$this->doctrineTypeMapping = [
'char' => 'string',
'long nvarchar' => 'text',
'long varchar' => 'text',
'nchar' => 'string',
'ntext' => 'text',
'nvarchar' => 'string',
'text' => 'text',
'uniqueidentifierstr' => 'guid',
'varchar' => 'string',
'xml' => 'text',
'bigint' => 'bigint',
'unsigned bigint' => 'bigint',
'bit' => 'boolean',
'decimal' => 'decimal',
'double' => 'float',
'float' => 'float',
'int' => 'integer',
'integer' => 'integer',
'unsigned int' => 'integer',
'numeric' => 'decimal',
'smallint' => 'smallint',
'unsigned smallint' => 'smallint',
'tinyint' => 'smallint',
'unsigned tinyint' => 'smallint',
'money' => 'decimal',
'smallmoney' => 'decimal',
'long varbit' => 'text',
'varbit' => 'string',
'date' => 'date',
'datetime' => 'datetime',
'smalldatetime' => 'datetime',
'time' => 'time',
'timestamp' => 'datetime',
'binary' => 'binary',
'image' => 'blob',
'long binary' => 'blob',
'uniqueidentifier' => 'guid',
'varbinary' => 'binary',
];
}
}
lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php 0000644 00000001635 13727250467 0015440 0 ustar 00 hasOption('azure.federatedOnColumnName')) {
$distributionName = $table->getOption('azure.federatedOnDistributionName');
$columnName = $table->getOption('azure.federatedOnColumnName');
$stmt = ' FEDERATED ON (' . $distributionName . ' = ' . $columnName . ')';
$sql[0] .= $stmt;
}
return $sql;
}
}
lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php 0000644 00000002214 13727250467 0016121 0 ustar 00 doctrineTypeMapping['datetime2'] = 'datetime';
$this->doctrineTypeMapping['date'] = 'date';
$this->doctrineTypeMapping['time'] = 'time';
$this->doctrineTypeMapping['datetimeoffset'] = 'datetimetz';
}
/**
* {@inheritdoc}
*
* Returns Microsoft SQL Server 2008 specific keywords class
*/
protected function getReservedKeywordsClass()
{
return Keywords\SQLServer2008Keywords::class;
}
protected function getLikeWildcardCharacters(): string
{
return parent::getLikeWildcardCharacters() . '[]^';
}
}
lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php 0000644 00000010703 13727250467 0016121 0 ustar 00 getQuotedName($this) .
' INCREMENT BY ' . $sequence->getAllocationSize();
}
/**
* {@inheritdoc}
*/
public function getCreateSequenceSQL(Sequence $sequence)
{
return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
' START WITH ' . $sequence->getInitialValue() .
' INCREMENT BY ' . $sequence->getAllocationSize() .
' MINVALUE ' . $sequence->getInitialValue();
}
/**
* {@inheritdoc}
*/
public function getDropSequenceSQL($sequence)
{
if ($sequence instanceof Sequence) {
$sequence = $sequence->getQuotedName($this);
}
return 'DROP SEQUENCE ' . $sequence;
}
/**
* {@inheritdoc}
*/
public function getListSequencesSQL($database)
{
return 'SELECT seq.name,
CAST(
seq.increment AS VARCHAR(MAX)
) AS increment, -- CAST avoids driver error for sql_variant type
CAST(
seq.start_value AS VARCHAR(MAX)
) AS start_value -- CAST avoids driver error for sql_variant type
FROM sys.sequences AS seq';
}
/**
* {@inheritdoc}
*/
public function getSequenceNextValSQL($sequence)
{
return 'SELECT NEXT VALUE FOR ' . $sequence;
}
/**
* {@inheritdoc}
*/
public function supportsSequences()
{
return true;
}
/**
* {@inheritdoc}
*
* Returns Microsoft SQL Server 2012 specific keywords class
*/
protected function getReservedKeywordsClass()
{
return Keywords\SQLServer2012Keywords::class;
}
/**
* {@inheritdoc}
*/
protected function doModifyLimitQuery($query, $limit, $offset = null)
{
if ($limit === null && $offset <= 0) {
return $query;
}
// Queries using OFFSET... FETCH MUST have an ORDER BY clause
// Find the position of the last instance of ORDER BY and ensure it is not within a parenthetical statement
// but can be in a newline
$matches = [];
$matchesCount = preg_match_all('/[\\s]+order\\s+by\\s/im', $query, $matches, PREG_OFFSET_CAPTURE);
$orderByPos = false;
if ($matchesCount > 0) {
$orderByPos = $matches[0][$matchesCount - 1][1];
}
if (
$orderByPos === false
|| substr_count($query, '(', $orderByPos) - substr_count($query, ')', $orderByPos)
) {
if (preg_match('/^SELECT\s+DISTINCT/im', $query)) {
// SQL Server won't let us order by a non-selected column in a DISTINCT query,
// so we have to do this madness. This says, order by the first column in the
// result. SQL Server's docs say that a nonordered query's result order is non-
// deterministic anyway, so this won't do anything that a bunch of update and
// deletes to the table wouldn't do anyway.
$query .= ' ORDER BY 1';
} else {
// In another DBMS, we could do ORDER BY 0, but SQL Server gets angry if you
// use constant expressions in the order by list.
$query .= ' ORDER BY (SELECT 0)';
}
}
if ($offset === null) {
$offset = 0;
}
// This looks somewhat like MYSQL, but limit/offset are in inverse positions
// Supposedly SQL:2008 core standard.
// Per TSQL spec, FETCH NEXT n ROWS ONLY is not valid without OFFSET n ROWS.
$query .= ' OFFSET ' . (int) $offset . ' ROWS';
if ($limit !== null) {
$query .= ' FETCH NEXT ' . (int) $limit . ' ROWS ONLY';
}
return $query;
}
}
lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php 0000644 00000147641 13727250467 0015630 0 ustar 00 getConvertExpression('date', 'GETDATE()');
}
/**
* {@inheritdoc}
*/
public function getCurrentTimeSQL()
{
return $this->getConvertExpression('time', 'GETDATE()');
}
/**
* Returns an expression that converts an expression of one data type to another.
*
* @param string $dataType The target native data type. Alias data types cannot be used.
* @param string $expression The SQL expression to convert.
*
* @return string
*/
private function getConvertExpression($dataType, $expression)
{
return sprintf('CONVERT(%s, %s)', $dataType, $expression);
}
/**
* {@inheritdoc}
*/
protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
{
$factorClause = '';
if ($operator === '-') {
$factorClause = '-1 * ';
}
return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')';
}
/**
* {@inheritDoc}
*/
public function getDateDiffExpression($date1, $date2)
{
return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')';
}
/**
* {@inheritDoc}
*
* Microsoft SQL Server prefers "autoincrement" identity columns
* since sequences can only be emulated with a table.
*/
public function prefersIdentityColumns()
{
return true;
}
/**
* {@inheritDoc}
*
* Microsoft SQL Server supports this through AUTO_INCREMENT columns.
*/
public function supportsIdentityColumns()
{
return true;
}
/**
* {@inheritDoc}
*/
public function supportsReleaseSavepoints()
{
return false;
}
/**
* {@inheritdoc}
*/
public function supportsSchemas()
{
return true;
}
/**
* {@inheritdoc}
*/
public function getDefaultSchemaName()
{
return 'dbo';
}
/**
* {@inheritDoc}
*/
public function supportsColumnCollation()
{
return true;
}
/**
* {@inheritDoc}
*/
public function hasNativeGuidType()
{
return true;
}
/**
* {@inheritDoc}
*/
public function getCreateDatabaseSQL($name)
{
return 'CREATE DATABASE ' . $name;
}
/**
* {@inheritDoc}
*/
public function getDropDatabaseSQL($name)
{
return 'DROP DATABASE ' . $name;
}
/**
* {@inheritDoc}
*/
public function supportsCreateDropDatabase()
{
return true;
}
/**
* {@inheritDoc}
*/
public function getCreateSchemaSQL($schemaName)
{
return 'CREATE SCHEMA ' . $schemaName;
}
/**
* {@inheritDoc}
*/
public function getDropForeignKeySQL($foreignKey, $table)
{
if (! $foreignKey instanceof ForeignKeyConstraint) {
$foreignKey = new Identifier($foreignKey);
}
if (! $table instanceof Table) {
$table = new Identifier($table);
}
$foreignKey = $foreignKey->getQuotedName($this);
$table = $table->getQuotedName($this);
return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey;
}
/**
* {@inheritDoc}
*/
public function getDropIndexSQL($index, $table = null)
{
if ($index instanceof Index) {
$index = $index->getQuotedName($this);
} elseif (! is_string($index)) {
throw new InvalidArgumentException(
__METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.'
);
}
if (! isset($table)) {
return 'DROP INDEX ' . $index;
}
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
}
return sprintf(
<<
* $qb = $conn->createQueryBuilder()
* ->select('u')
* ->from('users', 'u')
* ->where($qb->expr()->eq('u.id', 1));
*
*
* For more complex expression construction, consider storing the expression
* builder object in a local variable.
*
* @return ExpressionBuilder
*/
public function expr()
{
return $this->connection->getExpressionBuilder();
}
/**
* Gets the type of the currently built query.
*
* @return int
*/
public function getType()
{
return $this->type;
}
/**
* Gets the associated DBAL Connection for this query builder.
*
* @return Connection
*/
public function getConnection()
{
return $this->connection;
}
/**
* Gets the state of this query builder instance.
*
* @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
*/
public function getState()
{
return $this->state;
}
/**
* Executes this query using the bound parameters and their types.
*
* Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
* for insert, update and delete statements.
*
* @return ResultStatement|int
*/
public function execute()
{
if ($this->type === self::SELECT) {
return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes);
}
return $this->connection->executeUpdate($this->getSQL(), $this->params, $this->paramTypes);
}
/**
* Gets the complete SQL string formed by the current specifications of this QueryBuilder.
*
*
* $qb = $em->createQueryBuilder()
* ->select('u')
* ->from('User', 'u')
* echo $qb->getSQL(); // SELECT u FROM User u
*
*
* @return string The SQL query string.
*/
public function getSQL()
{
if ($this->sql !== null && $this->state === self::STATE_CLEAN) {
return $this->sql;
}
switch ($this->type) {
case self::INSERT:
$sql = $this->getSQLForInsert();
break;
case self::DELETE:
$sql = $this->getSQLForDelete();
break;
case self::UPDATE:
$sql = $this->getSQLForUpdate();
break;
case self::SELECT:
default:
$sql = $this->getSQLForSelect();
break;
}
$this->state = self::STATE_CLEAN;
$this->sql = $sql;
return $sql;
}
/**
* Sets a query parameter for the query being constructed.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u')
* ->from('users', 'u')
* ->where('u.id = :user_id')
* ->setParameter(':user_id', 1);
*
*
* @param string|int $key The parameter position or name.
* @param mixed $value The parameter value.
* @param string|int|null $type One of the {@link ParameterType} constants.
*
* @return $this This QueryBuilder instance.
*/
public function setParameter($key, $value, $type = null)
{
if ($type !== null) {
$this->paramTypes[$key] = $type;
}
$this->params[$key] = $value;
return $this;
}
/**
* Sets a collection of query parameters for the query being constructed.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u')
* ->from('users', 'u')
* ->where('u.id = :user_id1 OR u.id = :user_id2')
* ->setParameters(array(
* ':user_id1' => 1,
* ':user_id2' => 2
* ));
*
*
* @param mixed[] $params The query parameters to set.
* @param int[]|string[] $types The query parameters types to set.
*
* @return $this This QueryBuilder instance.
*/
public function setParameters(array $params, array $types = [])
{
$this->paramTypes = $types;
$this->params = $params;
return $this;
}
/**
* Gets all defined query parameters for the query being constructed indexed by parameter index or name.
*
* @return mixed[] The currently defined query parameters indexed by parameter index or name.
*/
public function getParameters()
{
return $this->params;
}
/**
* Gets a (previously set) query parameter of the query being constructed.
*
* @param mixed $key The key (index or name) of the bound parameter.
*
* @return mixed The value of the bound parameter.
*/
public function getParameter($key)
{
return $this->params[$key] ?? null;
}
/**
* Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
*
* @return int[]|string[] The currently defined query parameter types indexed by parameter index or name.
*/
public function getParameterTypes()
{
return $this->paramTypes;
}
/**
* Gets a (previously set) query parameter type of the query being constructed.
*
* @param mixed $key The key (index or name) of the bound parameter type.
*
* @return mixed The value of the bound parameter type.
*/
public function getParameterType($key)
{
return $this->paramTypes[$key] ?? null;
}
/**
* Sets the position of the first result to retrieve (the "offset").
*
* @param int $firstResult The first result to return.
*
* @return $this This QueryBuilder instance.
*/
public function setFirstResult($firstResult)
{
$this->state = self::STATE_DIRTY;
$this->firstResult = $firstResult;
return $this;
}
/**
* Gets the position of the first result the query object was set to retrieve (the "offset").
*
* @return int The position of the first result.
*/
public function getFirstResult()
{
return $this->firstResult;
}
/**
* Sets the maximum number of results to retrieve (the "limit").
*
* @param int|null $maxResults The maximum number of results to retrieve or NULL to retrieve all results.
*
* @return $this This QueryBuilder instance.
*/
public function setMaxResults($maxResults)
{
$this->state = self::STATE_DIRTY;
$this->maxResults = $maxResults;
return $this;
}
/**
* Gets the maximum number of results the query object was set to retrieve (the "limit").
* Returns NULL if all results will be returned.
*
* @return int|null The maximum number of results.
*/
public function getMaxResults()
{
return $this->maxResults;
}
/**
* Either appends to or replaces a single, generic query part.
*
* The available parts are: 'select', 'from', 'set', 'where',
* 'groupBy', 'having' and 'orderBy'.
*
* @param string $sqlPartName
* @param mixed $sqlPart
* @param bool $append
*
* @return $this This QueryBuilder instance.
*/
public function add($sqlPartName, $sqlPart, $append = false)
{
$isArray = is_array($sqlPart);
$isMultiple = is_array($this->sqlParts[$sqlPartName]);
if ($isMultiple && ! $isArray) {
$sqlPart = [$sqlPart];
}
$this->state = self::STATE_DIRTY;
if ($append) {
if (
$sqlPartName === 'orderBy'
|| $sqlPartName === 'groupBy'
|| $sqlPartName === 'select'
|| $sqlPartName === 'set'
) {
foreach ($sqlPart as $part) {
$this->sqlParts[$sqlPartName][] = $part;
}
} elseif ($isArray && is_array($sqlPart[key($sqlPart)])) {
$key = key($sqlPart);
$this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key];
} elseif ($isMultiple) {
$this->sqlParts[$sqlPartName][] = $sqlPart;
} else {
$this->sqlParts[$sqlPartName] = $sqlPart;
}
return $this;
}
$this->sqlParts[$sqlPartName] = $sqlPart;
return $this;
}
/**
* Specifies an item that is to be returned in the query result.
* Replaces any previously specified selections, if any.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u.id', 'p.id')
* ->from('users', 'u')
* ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
*
*
* @param mixed $select The selection expressions.
*
* @return $this This QueryBuilder instance.
*/
public function select($select = null)
{
$this->type = self::SELECT;
if (empty($select)) {
return $this;
}
$selects = is_array($select) ? $select : func_get_args();
return $this->add('select', $selects);
}
/**
* Adds DISTINCT to the query.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u.id')
* ->distinct()
* ->from('users', 'u')
*
*
* @return $this This QueryBuilder instance.
*/
public function distinct(): self
{
$this->sqlParts['distinct'] = true;
return $this;
}
/**
* Adds an item that is to be returned in the query result.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u.id')
* ->addSelect('p.id')
* ->from('users', 'u')
* ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
*
*
* @param mixed $select The selection expression.
*
* @return $this This QueryBuilder instance.
*/
public function addSelect($select = null)
{
$this->type = self::SELECT;
if (empty($select)) {
return $this;
}
$selects = is_array($select) ? $select : func_get_args();
return $this->add('select', $selects, true);
}
/**
* Turns the query being built into a bulk delete query that ranges over
* a certain table.
*
*
* $qb = $conn->createQueryBuilder()
* ->delete('users', 'u')
* ->where('u.id = :user_id')
* ->setParameter(':user_id', 1);
*
*
* @param string $delete The table whose rows are subject to the deletion.
* @param string $alias The table alias used in the constructed query.
*
* @return $this This QueryBuilder instance.
*/
public function delete($delete = null, $alias = null)
{
$this->type = self::DELETE;
if (! $delete) {
return $this;
}
return $this->add('from', [
'table' => $delete,
'alias' => $alias,
]);
}
/**
* Turns the query being built into a bulk update query that ranges over
* a certain table
*
*
* $qb = $conn->createQueryBuilder()
* ->update('counters', 'c')
* ->set('c.value', 'c.value + 1')
* ->where('c.id = ?');
*
*
* @param string $update The table whose rows are subject to the update.
* @param string $alias The table alias used in the constructed query.
*
* @return $this This QueryBuilder instance.
*/
public function update($update = null, $alias = null)
{
$this->type = self::UPDATE;
if (! $update) {
return $this;
}
return $this->add('from', [
'table' => $update,
'alias' => $alias,
]);
}
/**
* Turns the query being built into an insert query that inserts into
* a certain table
*
*
* $qb = $conn->createQueryBuilder()
* ->insert('users')
* ->values(
* array(
* 'name' => '?',
* 'password' => '?'
* )
* );
*
*
* @param string $insert The table into which the rows should be inserted.
*
* @return $this This QueryBuilder instance.
*/
public function insert($insert = null)
{
$this->type = self::INSERT;
if (! $insert) {
return $this;
}
return $this->add('from', ['table' => $insert]);
}
/**
* Creates and adds a query root corresponding to the table identified by the
* given alias, forming a cartesian product with any existing query roots.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u.id')
* ->from('users', 'u')
*
*
* @param string $from The table.
* @param string|null $alias The alias of the table.
*
* @return $this This QueryBuilder instance.
*/
public function from($from, $alias = null)
{
return $this->add('from', [
'table' => $from,
'alias' => $alias,
], true);
}
/**
* Creates and adds a join to the query.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u.name')
* ->from('users', 'u')
* ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
*
*
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
*
* @return $this This QueryBuilder instance.
*/
public function join($fromAlias, $join, $alias, $condition = null)
{
return $this->innerJoin($fromAlias, $join, $alias, $condition);
}
/**
* Creates and adds a join to the query.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u.name')
* ->from('users', 'u')
* ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
*
*
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
*
* @return $this This QueryBuilder instance.
*/
public function innerJoin($fromAlias, $join, $alias, $condition = null)
{
return $this->add('join', [
$fromAlias => [
'joinType' => 'inner',
'joinTable' => $join,
'joinAlias' => $alias,
'joinCondition' => $condition,
],
], true);
}
/**
* Creates and adds a left join to the query.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u.name')
* ->from('users', 'u')
* ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
*
*
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
*
* @return $this This QueryBuilder instance.
*/
public function leftJoin($fromAlias, $join, $alias, $condition = null)
{
return $this->add('join', [
$fromAlias => [
'joinType' => 'left',
'joinTable' => $join,
'joinAlias' => $alias,
'joinCondition' => $condition,
],
], true);
}
/**
* Creates and adds a right join to the query.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u.name')
* ->from('users', 'u')
* ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
*
*
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
* @param string $condition The condition for the join.
*
* @return $this This QueryBuilder instance.
*/
public function rightJoin($fromAlias, $join, $alias, $condition = null)
{
return $this->add('join', [
$fromAlias => [
'joinType' => 'right',
'joinTable' => $join,
'joinAlias' => $alias,
'joinCondition' => $condition,
],
], true);
}
/**
* Sets a new value for a column in a bulk update query.
*
*
* $qb = $conn->createQueryBuilder()
* ->update('counters', 'c')
* ->set('c.value', 'c.value + 1')
* ->where('c.id = ?');
*
*
* @param string $key The column to set.
* @param string $value The value, expression, placeholder, etc.
*
* @return $this This QueryBuilder instance.
*/
public function set($key, $value)
{
return $this->add('set', $key . ' = ' . $value, true);
}
/**
* Specifies one or more restrictions to the query result.
* Replaces any previously specified restrictions, if any.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('c.value')
* ->from('counters', 'c')
* ->where('c.id = ?');
*
* // You can optionally programatically build and/or expressions
* $qb = $conn->createQueryBuilder();
*
* $or = $qb->expr()->orx();
* $or->add($qb->expr()->eq('c.id', 1));
* $or->add($qb->expr()->eq('c.id', 2));
*
* $qb->update('counters', 'c')
* ->set('c.value', 'c.value + 1')
* ->where($or);
*
*
* @param mixed $predicates The restriction predicates.
*
* @return $this This QueryBuilder instance.
*/
public function where($predicates)
{
if (! (func_num_args() === 1 && $predicates instanceof CompositeExpression)) {
$predicates = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
}
return $this->add('where', $predicates);
}
/**
* Adds one or more restrictions to the query results, forming a logical
* conjunction with any previously specified restrictions.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u')
* ->from('users', 'u')
* ->where('u.username LIKE ?')
* ->andWhere('u.is_active = 1');
*
*
* @see where()
*
* @param mixed $where The query restrictions.
*
* @return $this This QueryBuilder instance.
*/
public function andWhere($where)
{
$args = func_get_args();
$where = $this->getQueryPart('where');
if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) {
$where->addMultiple($args);
} else {
array_unshift($args, $where);
$where = new CompositeExpression(CompositeExpression::TYPE_AND, $args);
}
return $this->add('where', $where, true);
}
/**
* Adds one or more restrictions to the query results, forming a logical
* disjunction with any previously specified restrictions.
*
*
* $qb = $em->createQueryBuilder()
* ->select('u.name')
* ->from('users', 'u')
* ->where('u.id = 1')
* ->orWhere('u.id = 2');
*
*
* @see where()
*
* @param mixed $where The WHERE statement.
*
* @return $this This QueryBuilder instance.
*/
public function orWhere($where)
{
$args = func_get_args();
$where = $this->getQueryPart('where');
if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) {
$where->addMultiple($args);
} else {
array_unshift($args, $where);
$where = new CompositeExpression(CompositeExpression::TYPE_OR, $args);
}
return $this->add('where', $where, true);
}
/**
* Specifies a grouping over the results of the query.
* Replaces any previously specified groupings, if any.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u.name')
* ->from('users', 'u')
* ->groupBy('u.id');
*
*
* @param mixed $groupBy The grouping expression.
*
* @return $this This QueryBuilder instance.
*/
public function groupBy($groupBy)
{
if (empty($groupBy)) {
return $this;
}
$groupBy = is_array($groupBy) ? $groupBy : func_get_args();
return $this->add('groupBy', $groupBy, false);
}
/**
* Adds a grouping expression to the query.
*
*
* $qb = $conn->createQueryBuilder()
* ->select('u.name')
* ->from('users', 'u')
* ->groupBy('u.lastLogin')
* ->addGroupBy('u.createdAt');
*
*
* @param mixed $groupBy The grouping expression.
*
* @return $this This QueryBuilder instance.
*/
public function addGroupBy($groupBy)
{
if (empty($groupBy)) {
return $this;
}
$groupBy = is_array($groupBy) ? $groupBy : func_get_args();
return $this->add('groupBy', $groupBy, true);
}
/**
* Sets a value for a column in an insert query.
*
*
* $qb = $conn->createQueryBuilder()
* ->insert('users')
* ->values(
* array(
* 'name' => '?'
* )
* )
* ->setValue('password', '?');
*
*
* @param string $column The column into which the value should be inserted.
* @param string $value The value that should be inserted into the column.
*
* @return $this This QueryBuilder instance.
*/
public function setValue($column, $value)
{
$this->sqlParts['values'][$column] = $value;
return $this;
}
/**
* Specifies values for an insert query indexed by column names.
* Replaces any previous values, if any.
*
*
* $qb = $conn->createQueryBuilder()
* ->insert('users')
* ->values(
* array(
* 'name' => '?',
* 'password' => '?'
* )
* );
*
*
* @param mixed[] $values The values to specify for the insert query indexed by column names.
*
* @return $this This QueryBuilder instance.
*/
public function values(array $values)
{
return $this->add('values', $values);
}
/**
* Specifies a restriction over the groups of the query.
* Replaces any previous having restrictions, if any.
*
* @param mixed $having The restriction over the groups.
*
* @return $this This QueryBuilder instance.
*/
public function having($having)
{
if (! (func_num_args() === 1 && $having instanceof CompositeExpression)) {
$having = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
}
return $this->add('having', $having);
}
/**
* Adds a restriction over the groups of the query, forming a logical
* conjunction with any existing having restrictions.
*
* @param mixed $having The restriction to append.
*
* @return $this This QueryBuilder instance.
*/
public function andHaving($having)
{
$args = func_get_args();
$having = $this->getQueryPart('having');
if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) {
$having->addMultiple($args);
} else {
array_unshift($args, $having);
$having = new CompositeExpression(CompositeExpression::TYPE_AND, $args);
}
return $this->add('having', $having);
}
/**
* Adds a restriction over the groups of the query, forming a logical
* disjunction with any existing having restrictions.
*
* @param mixed $having The restriction to add.
*
* @return $this This QueryBuilder instance.
*/
public function orHaving($having)
{
$args = func_get_args();
$having = $this->getQueryPart('having');
if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) {
$having->addMultiple($args);
} else {
array_unshift($args, $having);
$having = new CompositeExpression(CompositeExpression::TYPE_OR, $args);
}
return $this->add('having', $having);
}
/**
* Specifies an ordering for the query results.
* Replaces any previously specified orderings, if any.
*
* @param string $sort The ordering expression.
* @param string $order The ordering direction.
*
* @return $this This QueryBuilder instance.
*/
public function orderBy($sort, $order = null)
{
return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), false);
}
/**
* Adds an ordering to the query results.
*
* @param string $sort The ordering expression.
* @param string $order The ordering direction.
*
* @return $this This QueryBuilder instance.
*/
public function addOrderBy($sort, $order = null)
{
return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true);
}
/**
* Gets a query part by its name.
*
* @param string $queryPartName
*
* @return mixed
*/
public function getQueryPart($queryPartName)
{
return $this->sqlParts[$queryPartName];
}
/**
* Gets all query parts.
*
* @return mixed[]
*/
public function getQueryParts()
{
return $this->sqlParts;
}
/**
* Resets SQL parts.
*
* @param string[]|null $queryPartNames
*
* @return $this This QueryBuilder instance.
*/
public function resetQueryParts($queryPartNames = null)
{
if ($queryPartNames === null) {
$queryPartNames = array_keys($this->sqlParts);
}
foreach ($queryPartNames as $queryPartName) {
$this->resetQueryPart($queryPartName);
}
return $this;
}
/**
* Resets a single SQL part.
*
* @param string $queryPartName
*
* @return $this This QueryBuilder instance.
*/
public function resetQueryPart($queryPartName)
{
$this->sqlParts[$queryPartName] = self::SQL_PARTS_DEFAULTS[$queryPartName];
$this->state = self::STATE_DIRTY;
return $this;
}
/**
* @return string
*
* @throws QueryException
*/
private function getSQLForSelect()
{
$query = 'SELECT ' . ($this->sqlParts['distinct'] ? 'DISTINCT ' : '') .
implode(', ', $this->sqlParts['select']);
$query .= ($this->sqlParts['from'] ? ' FROM ' . implode(', ', $this->getFromClauses()) : '')
. ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '')
. ($this->sqlParts['groupBy'] ? ' GROUP BY ' . implode(', ', $this->sqlParts['groupBy']) : '')
. ($this->sqlParts['having'] !== null ? ' HAVING ' . ((string) $this->sqlParts['having']) : '')
. ($this->sqlParts['orderBy'] ? ' ORDER BY ' . implode(', ', $this->sqlParts['orderBy']) : '');
if ($this->isLimitQuery()) {
return $this->connection->getDatabasePlatform()->modifyLimitQuery(
$query,
$this->maxResults,
$this->firstResult
);
}
return $query;
}
/**
* @return string[]
*/
private function getFromClauses()
{
$fromClauses = [];
$knownAliases = [];
// Loop through all FROM clauses
foreach ($this->sqlParts['from'] as $from) {
if ($from['alias'] === null) {
$tableSql = $from['table'];
$tableReference = $from['table'];
} else {
$tableSql = $from['table'] . ' ' . $from['alias'];
$tableReference = $from['alias'];
}
$knownAliases[$tableReference] = true;
$fromClauses[$tableReference] = $tableSql . $this->getSQLForJoins($tableReference, $knownAliases);
}
$this->verifyAllAliasesAreKnown($knownAliases);
return $fromClauses;
}
/**
* @param string[] $knownAliases
*
* @throws QueryException
*/
private function verifyAllAliasesAreKnown(array $knownAliases): void
{
foreach ($this->sqlParts['join'] as $fromAlias => $joins) {
if (! isset($knownAliases[$fromAlias])) {
throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases));
}
}
}
/**
* @return bool
*/
private function isLimitQuery()
{
return $this->maxResults !== null || $this->firstResult !== null;
}
/**
* Converts this instance into an INSERT string in SQL.
*
* @return string
*/
private function getSQLForInsert()
{
return 'INSERT INTO ' . $this->sqlParts['from']['table'] .
' (' . implode(', ', array_keys($this->sqlParts['values'])) . ')' .
' VALUES(' . implode(', ', $this->sqlParts['values']) . ')';
}
/**
* Converts this instance into an UPDATE string in SQL.
*
* @return string
*/
private function getSQLForUpdate()
{
$table = $this->sqlParts['from']['table']
. ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
return 'UPDATE ' . $table
. ' SET ' . implode(', ', $this->sqlParts['set'])
. ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
}
/**
* Converts this instance into a DELETE string in SQL.
*
* @return string
*/
private function getSQLForDelete()
{
$table = $this->sqlParts['from']['table']
. ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
return 'DELETE FROM ' . $table
. ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
}
/**
* Gets a string representation of this QueryBuilder which corresponds to
* the final SQL query being constructed.
*
* @return string The string representation of this QueryBuilder.
*/
public function __toString()
{
return $this->getSQL();
}
/**
* Creates a new named parameter and bind the value $value to it.
*
* This method provides a shortcut for PDOStatement::bindValue
* when using prepared statements.
*
* The parameter $value specifies the value that you want to bind. If
* $placeholder is not provided bindValue() will automatically create a
* placeholder for you. An automatic placeholder will be of the name
* ':dcValue1', ':dcValue2' etc.
*
* For more information see {@link http://php.net/pdostatement-bindparam}
*
* Example:
*
* $value = 2;
* $q->eq( 'id', $q->bindValue( $value ) );
* $stmt = $q->executeQuery(); // executed with 'id = 2'
*
*
* @link http://www.zetacomponents.org
*
* @param mixed $value
* @param mixed $type
* @param string $placeHolder The name to bind with. The string must start with a colon ':'.
*
* @return string the placeholder name used.
*/
public function createNamedParameter($value, $type = ParameterType::STRING, $placeHolder = null)
{
if ($placeHolder === null) {
$this->boundCounter++;
$placeHolder = ':dcValue' . $this->boundCounter;
}
$this->setParameter(substr($placeHolder, 1), $value, $type);
return $placeHolder;
}
/**
* Creates a new positional parameter and bind the given value to it.
*
* Attention: If you are using positional parameters with the query builder you have
* to be very careful to bind all parameters in the order they appear in the SQL
* statement , otherwise they get bound in the wrong order which can lead to serious
* bugs in your code.
*
* Example:
*
* $qb = $conn->createQueryBuilder();
* $qb->select('u.*')
* ->from('users', 'u')
* ->where('u.username = ' . $qb->createPositionalParameter('Foo', ParameterType::STRING))
* ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', ParameterType::STRING))
*
*
* @param mixed $value
* @param int $type
*
* @return string
*/
public function createPositionalParameter($value, $type = ParameterType::STRING)
{
$this->boundCounter++;
$this->setParameter($this->boundCounter, $value, $type);
return '?';
}
/**
* @param string $fromAlias
* @param string[] $knownAliases
*
* @return string
*
* @throws QueryException
*/
private function getSQLForJoins($fromAlias, array &$knownAliases)
{
$sql = '';
if (isset($this->sqlParts['join'][$fromAlias])) {
foreach ($this->sqlParts['join'][$fromAlias] as $join) {
if (array_key_exists($join['joinAlias'], $knownAliases)) {
throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases));
}
$sql .= ' ' . strtoupper($join['joinType'])
. ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'];
if ($join['joinCondition'] !== null) {
$sql .= ' ON ' . $join['joinCondition'];
}
$knownAliases[$join['joinAlias']] = true;
}
foreach ($this->sqlParts['join'][$fromAlias] as $join) {
$sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases);
}
}
return $sql;
}
/**
* Deep clone of all expression objects in the SQL parts.
*
* @return void
*/
public function __clone()
{
foreach ($this->sqlParts as $part => $elements) {
if (is_array($this->sqlParts[$part])) {
foreach ($this->sqlParts[$part] as $idx => $element) {
if (! is_object($element)) {
continue;
}
$this->sqlParts[$part][$idx] = clone $element;
}
} elseif (is_object($elements)) {
$this->sqlParts[$part] = clone $elements;
}
}
foreach ($this->params as $name => $param) {
if (! is_object($param)) {
continue;
}
$this->params[$name] = clone $param;
}
}
}
lib/Doctrine/DBAL/Query/QueryException.php 0000644 00000002003 13727250467 0014375 0 ustar 00 $types The types the previous parameters are in.
*
* @return mixed[]
*
* @throws SQLParserUtilsException
*/
public static function expandListParameters($query, $params, $types)
{
$isPositional = is_int(key($params));
$arrayPositions = [];
$bindIndex = -1;
if ($isPositional) {
// make sure that $types has the same keys as $params
// to allow omitting parameters with unspecified types
$types += array_fill_keys(array_keys($params), null);
ksort($params);
ksort($types);
}
foreach ($types as $name => $type) {
++$bindIndex;
if ($type !== Connection::PARAM_INT_ARRAY && $type !== Connection::PARAM_STR_ARRAY) {
continue;
}
if ($isPositional) {
$name = $bindIndex;
}
$arrayPositions[$name] = false;
}
if (( ! $arrayPositions && $isPositional)) {
return [$query, $params, $types];
}
if ($isPositional) {
$paramOffset = 0;
$queryOffset = 0;
$params = array_values($params);
$types = array_values($types);
$paramPos = self::getPositionalPlaceholderPositions($query);
foreach ($paramPos as $needle => $needlePos) {
if (! isset($arrayPositions[$needle])) {
continue;
}
$needle += $paramOffset;
$needlePos += $queryOffset;
$count = count($params[$needle]);
$params = array_merge(
array_slice($params, 0, $needle),
$params[$needle],
array_slice($params, $needle + 1)
);
$types = array_merge(
array_slice($types, 0, $needle),
$count ?
// array needles are at {@link \Doctrine\DBAL\ParameterType} constants
// + {@link \Doctrine\DBAL\Connection::ARRAY_PARAM_OFFSET}
array_fill(0, $count, $types[$needle] - Connection::ARRAY_PARAM_OFFSET) :
[],
array_slice($types, $needle + 1)
);
$expandStr = $count ? implode(', ', array_fill(0, $count, '?')) : 'NULL';
$query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1);
$paramOffset += $count - 1; // Grows larger by number of parameters minus the replaced needle.
$queryOffset += strlen($expandStr) - 1;
}
return [$query, $params, $types];
}
$queryOffset = 0;
$typesOrd = [];
$paramsOrd = [];
$paramPos = self::getNamedPlaceholderPositions($query);
foreach ($paramPos as $pos => $paramName) {
$paramLen = strlen($paramName) + 1;
$value = static::extractParam($paramName, $params, true);
if (! isset($arrayPositions[$paramName]) && ! isset($arrayPositions[':' . $paramName])) {
$pos += $queryOffset;
$queryOffset -= $paramLen - 1;
$paramsOrd[] = $value;
$typesOrd[] = static::extractParam($paramName, $types, false, ParameterType::STRING);
$query = substr($query, 0, $pos) . '?' . substr($query, $pos + $paramLen);
continue;
}
$count = count($value);
$expandStr = $count > 0 ? implode(', ', array_fill(0, $count, '?')) : 'NULL';
foreach ($value as $val) {
$paramsOrd[] = $val;
$typesOrd[] = static::extractParam($paramName, $types, false) - Connection::ARRAY_PARAM_OFFSET;
}
$pos += $queryOffset;
$queryOffset += strlen($expandStr) - $paramLen;
$query = substr($query, 0, $pos) . $expandStr . substr($query, $pos + $paramLen);
}
return [$query, $paramsOrd, $typesOrd];
}
/**
* Slice the SQL statement around pairs of quotes and
* return string fragments of SQL outside of quoted literals.
* Each fragment is captured as a 2-element array:
*
* 0 => matched fragment string,
* 1 => offset of fragment in $statement
*
* @param string $statement
*
* @return mixed[][]
*/
private static function getUnquotedStatementFragments($statement)
{
$literal = self::ESCAPED_SINGLE_QUOTED_TEXT . '|' .
self::ESCAPED_DOUBLE_QUOTED_TEXT . '|' .
self::ESCAPED_BACKTICK_QUOTED_TEXT . '|' .
self::ESCAPED_BRACKET_QUOTED_TEXT;
$expression = sprintf('/((.+(?i:ARRAY)\\[.+\\])|([^\'"`\\[]+))(?:%s)?/s', $literal);
preg_match_all($expression, $statement, $fragments, PREG_OFFSET_CAPTURE);
return $fragments[1];
}
/**
* @param string $paramName The name of the parameter (without a colon in front)
* @param mixed $paramsOrTypes A hash of parameters or types
* @param bool $isParam
* @param mixed $defaultValue An optional default value. If omitted, an exception is thrown
*
* @return mixed
*
* @throws SQLParserUtilsException
*/
private static function extractParam($paramName, $paramsOrTypes, $isParam, $defaultValue = null)
{
if (array_key_exists($paramName, $paramsOrTypes)) {
return $paramsOrTypes[$paramName];
}
// Hash keys can be prefixed with a colon for compatibility
if (array_key_exists(':' . $paramName, $paramsOrTypes)) {
return $paramsOrTypes[':' . $paramName];
}
if ($defaultValue !== null) {
return $defaultValue;
}
if ($isParam) {
throw SQLParserUtilsException::missingParam($paramName);
}
throw SQLParserUtilsException::missingType($paramName);
}
}
lib/Doctrine/DBAL/SQLParserUtilsException.php 0000644 00000001423 13727250467 0015025 0 ustar 00 Table($tableName)); if you want to rename the table, you have to make sure
*/
abstract class AbstractAsset
{
/** @var string */
protected $_name;
/**
* Namespace of the asset. If none isset the default namespace is assumed.
*
* @var string|null
*/
protected $_namespace = null;
/** @var bool */
protected $_quoted = false;
/**
* Sets the name of this asset.
*
* @param string $name
*
* @return void
*/
protected function _setName($name)
{
if ($this->isIdentifierQuoted($name)) {
$this->_quoted = true;
$name = $this->trimQuotes($name);
}
if (strpos($name, '.') !== false) {
$parts = explode('.', $name);
$this->_namespace = $parts[0];
$name = $parts[1];
}
$this->_name = $name;
}
/**
* Is this asset in the default namespace?
*
* @param string $defaultNamespaceName
*
* @return bool
*/
public function isInDefaultNamespace($defaultNamespaceName)
{
return $this->_namespace === $defaultNamespaceName || $this->_namespace === null;
}
/**
* Gets the namespace name of this asset.
*
* If NULL is returned this means the default namespace is used.
*
* @return string|null
*/
public function getNamespaceName()
{
return $this->_namespace;
}
/**
* The shortest name is stripped of the default namespace. All other
* namespaced elements are returned as full-qualified names.
*
* @param string|null $defaultNamespaceName
*
* @return string
*/
public function getShortestName($defaultNamespaceName)
{
$shortestName = $this->getName();
if ($this->_namespace === $defaultNamespaceName) {
$shortestName = $this->_name;
}
return strtolower($shortestName);
}
/**
* The normalized name is full-qualified and lowerspaced. Lowerspacing is
* actually wrong, but we have to do it to keep our sanity. If you are
* using database objects that only differentiate in the casing (FOO vs
* Foo) then you will NOT be able to use Doctrine Schema abstraction.
*
* Every non-namespaced element is prefixed with the default namespace
* name which is passed as argument to this method.
*
* @param string $defaultNamespaceName
*
* @return string
*/
public function getFullQualifiedName($defaultNamespaceName)
{
$name = $this->getName();
if (! $this->_namespace) {
$name = $defaultNamespaceName . '.' . $name;
}
return strtolower($name);
}
/**
* Checks if this asset's name is quoted.
*
* @return bool
*/
public function isQuoted()
{
return $this->_quoted;
}
/**
* Checks if this identifier is quoted.
*
* @param string $identifier
*
* @return bool
*/
protected function isIdentifierQuoted($identifier)
{
return isset($identifier[0]) && ($identifier[0] === '`' || $identifier[0] === '"' || $identifier[0] === '[');
}
/**
* Trim quotes from the identifier.
*
* @param string $identifier
*
* @return string
*/
protected function trimQuotes($identifier)
{
return str_replace(['`', '"', '[', ']'], '', $identifier);
}
/**
* Returns the name of this schema asset.
*
* @return string
*/
public function getName()
{
if ($this->_namespace) {
return $this->_namespace . '.' . $this->_name;
}
return $this->_name;
}
/**
* Gets the quoted representation of this asset but only if it was defined with one. Otherwise
* return the plain unquoted value as inserted.
*
* @return string
*/
public function getQuotedName(AbstractPlatform $platform)
{
$keywords = $platform->getReservedKeywordsList();
$parts = explode('.', $this->getName());
foreach ($parts as $k => $v) {
$parts[$k] = $this->_quoted || $keywords->isKeyword($v) ? $platform->quoteIdentifier($v) : $v;
}
return implode('.', $parts);
}
/**
* Generates an identifier from a list of column names obeying a certain string length.
*
* This is especially important for Oracle, since it does not allow identifiers larger than 30 chars,
* however building idents automatically for foreign keys, composite keys or such can easily create
* very long names.
*
* @param string[] $columnNames
* @param string $prefix
* @param int $maxSize
*
* @return string
*/
protected function _generateIdentifierName($columnNames, $prefix = '', $maxSize = 30)
{
$hash = implode('', array_map(static function ($column) {
return dechex(crc32($column));
}, $columnNames));
return strtoupper(substr($prefix . '_' . $hash, 0, $maxSize));
}
}
lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php 0000644 00000072175 13727250467 0015704 0 ustar 00 _conn = $conn;
$this->_platform = $platform ?: $this->_conn->getDatabasePlatform();
}
/**
* Returns the associated platform.
*
* @return AbstractPlatform
*/
public function getDatabasePlatform()
{
return $this->_platform;
}
/**
* Tries any method on the schema manager. Normally a method throws an
* exception when your DBMS doesn't support it or if an error occurs.
* This method allows you to try and method on your SchemaManager
* instance and will return false if it does not work or is not supported.
*
*
* $result = $sm->tryMethod('dropView', 'view_name');
*
*
* @return mixed
*/
public function tryMethod()
{
$args = func_get_args();
$method = $args[0];
unset($args[0]);
$args = array_values($args);
$callback = [$this, $method];
assert(is_callable($callback));
try {
return call_user_func_array($callback, $args);
} catch (Throwable $e) {
return false;
}
}
/**
* Lists the available databases for this connection.
*
* @return string[]
*/
public function listDatabases()
{
$sql = $this->_platform->getListDatabasesSQL();
$databases = $this->_conn->fetchAll($sql);
return $this->_getPortableDatabasesList($databases);
}
/**
* Returns a list of all namespaces in the current database.
*
* @return string[]
*/
public function listNamespaceNames()
{
$sql = $this->_platform->getListNamespacesSQL();
$namespaces = $this->_conn->fetchAll($sql);
return $this->getPortableNamespacesList($namespaces);
}
/**
* Lists the available sequences for this connection.
*
* @param string|null $database
*
* @return Sequence[]
*/
public function listSequences($database = null)
{
if ($database === null) {
$database = $this->_conn->getDatabase();
}
$sql = $this->_platform->getListSequencesSQL($database);
$sequences = $this->_conn->fetchAll($sql);
return $this->filterAssetNames($this->_getPortableSequencesList($sequences));
}
/**
* Lists the columns for a given table.
*
* In contrast to other libraries and to the old version of Doctrine,
* this column definition does try to contain the 'primary' column for
* the reason that it is not portable across different RDBMS. Use
* {@see listTableIndexes($tableName)} to retrieve the primary key
* of a table. Where a RDBMS specifies more details, these are held
* in the platformDetails array.
*
* @param string $table The name of the table.
* @param string|null $database
*
* @return Column[]
*/
public function listTableColumns($table, $database = null)
{
if (! $database) {
$database = $this->_conn->getDatabase();
}
$sql = $this->_platform->getListTableColumnsSQL($table, $database);
$tableColumns = $this->_conn->fetchAll($sql);
return $this->_getPortableTableColumnList($table, $database, $tableColumns);
}
/**
* Lists the indexes for a given table returning an array of Index instances.
*
* Keys of the portable indexes list are all lower-cased.
*
* @param string $table The name of the table.
*
* @return Index[]
*/
public function listTableIndexes($table)
{
$sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase());
$tableIndexes = $this->_conn->fetchAll($sql);
return $this->_getPortableTableIndexesList($tableIndexes, $table);
}
/**
* Returns true if all the given tables exist.
*
* The usage of a string $tableNames is deprecated. Pass a one-element array instead.
*
* @param string|string[] $names
*
* @return bool
*/
public function tablesExist($names)
{
$names = array_map('strtolower', (array) $names);
return count($names) === count(array_intersect($names, array_map('strtolower', $this->listTableNames())));
}
/**
* Returns a list of all tables in the current database.
*
* @return string[]
*/
public function listTableNames()
{
$sql = $this->_platform->getListTablesSQL();
$tables = $this->_conn->fetchAll($sql);
$tableNames = $this->_getPortableTablesList($tables);
return $this->filterAssetNames($tableNames);
}
/**
* Filters asset names if they are configured to return only a subset of all
* the found elements.
*
* @param mixed[] $assetNames
*
* @return mixed[]
*/
protected function filterAssetNames($assetNames)
{
$filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter();
if (! $filter) {
return $assetNames;
}
return array_values(array_filter($assetNames, $filter));
}
/**
* @deprecated Use Configuration::getSchemaAssetsFilter() instead
*
* @return string|null
*/
protected function getFilterSchemaAssetsExpression()
{
return $this->_conn->getConfiguration()->getFilterSchemaAssetsExpression();
}
/**
* Lists the tables for this connection.
*
* @return Table[]
*/
public function listTables()
{
$tableNames = $this->listTableNames();
$tables = [];
foreach ($tableNames as $tableName) {
$tables[] = $this->listTableDetails($tableName);
}
return $tables;
}
/**
* @param string $name
*
* @return Table
*/
public function listTableDetails($name)
{
$columns = $this->listTableColumns($name);
$foreignKeys = [];
if ($this->_platform->supportsForeignKeyConstraints()) {
$foreignKeys = $this->listTableForeignKeys($name);
}
$indexes = $this->listTableIndexes($name);
return new Table($name, $columns, $indexes, $foreignKeys);
}
/**
* Lists the views this connection has.
*
* @return View[]
*/
public function listViews()
{
$database = $this->_conn->getDatabase();
$sql = $this->_platform->getListViewsSQL($database);
$views = $this->_conn->fetchAll($sql);
return $this->_getPortableViewsList($views);
}
/**
* Lists the foreign keys for the given table.
*
* @param string $table The name of the table.
* @param string|null $database
*
* @return ForeignKeyConstraint[]
*/
public function listTableForeignKeys($table, $database = null)
{
if ($database === null) {
$database = $this->_conn->getDatabase();
}
$sql = $this->_platform->getListTableForeignKeysSQL($table, $database);
$tableForeignKeys = $this->_conn->fetchAll($sql);
return $this->_getPortableTableForeignKeysList($tableForeignKeys);
}
/* drop*() Methods */
/**
* Drops a database.
*
* NOTE: You can not drop the database this SchemaManager is currently connected to.
*
* @param string $database The name of the database to drop.
*
* @return void
*/
public function dropDatabase($database)
{
$this->_execSql($this->_platform->getDropDatabaseSQL($database));
}
/**
* Drops the given table.
*
* @param string $name The name of the table to drop.
*
* @return void
*/
public function dropTable($name)
{
$this->_execSql($this->_platform->getDropTableSQL($name));
}
/**
* Drops the index from the given table.
*
* @param Index|string $index The name of the index.
* @param Table|string $table The name of the table.
*
* @return void
*/
public function dropIndex($index, $table)
{
if ($index instanceof Index) {
$index = $index->getQuotedName($this->_platform);
}
$this->_execSql($this->_platform->getDropIndexSQL($index, $table));
}
/**
* Drops the constraint from the given table.
*
* @param Table|string $table The name of the table.
*
* @return void
*/
public function dropConstraint(Constraint $constraint, $table)
{
$this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table));
}
/**
* Drops a foreign key from a table.
*
* @param ForeignKeyConstraint|string $foreignKey The name of the foreign key.
* @param Table|string $table The name of the table with the foreign key.
*
* @return void
*/
public function dropForeignKey($foreignKey, $table)
{
$this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table));
}
/**
* Drops a sequence with a given name.
*
* @param string $name The name of the sequence to drop.
*
* @return void
*/
public function dropSequence($name)
{
$this->_execSql($this->_platform->getDropSequenceSQL($name));
}
/**
* Drops a view.
*
* @param string $name The name of the view.
*
* @return void
*/
public function dropView($name)
{
$this->_execSql($this->_platform->getDropViewSQL($name));
}
/* create*() Methods */
/**
* Creates a new database.
*
* @param string $database The name of the database to create.
*
* @return void
*/
public function createDatabase($database)
{
$this->_execSql($this->_platform->getCreateDatabaseSQL($database));
}
/**
* Creates a new table.
*
* @return void
*/
public function createTable(Table $table)
{
$createFlags = AbstractPlatform::CREATE_INDEXES | AbstractPlatform::CREATE_FOREIGNKEYS;
$this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags));
}
/**
* Creates a new sequence.
*
* @param Sequence $sequence
*
* @return void
*
* @throws ConnectionException If something fails at database level.
*/
public function createSequence($sequence)
{
$this->_execSql($this->_platform->getCreateSequenceSQL($sequence));
}
/**
* Creates a constraint on a table.
*
* @param Table|string $table
*
* @return void
*/
public function createConstraint(Constraint $constraint, $table)
{
$this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table));
}
/**
* Creates a new index on a table.
*
* @param Table|string $table The name of the table on which the index is to be created.
*
* @return void
*/
public function createIndex(Index $index, $table)
{
$this->_execSql($this->_platform->getCreateIndexSQL($index, $table));
}
/**
* Creates a new foreign key.
*
* @param ForeignKeyConstraint $foreignKey The ForeignKey instance.
* @param Table|string $table The name of the table on which the foreign key is to be created.
*
* @return void
*/
public function createForeignKey(ForeignKeyConstraint $foreignKey, $table)
{
$this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table));
}
/**
* Creates a new view.
*
* @return void
*/
public function createView(View $view)
{
$this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql()));
}
/* dropAndCreate*() Methods */
/**
* Drops and creates a constraint.
*
* @see dropConstraint()
* @see createConstraint()
*
* @param Table|string $table
*
* @return void
*/
public function dropAndCreateConstraint(Constraint $constraint, $table)
{
$this->tryMethod('dropConstraint', $constraint, $table);
$this->createConstraint($constraint, $table);
}
/**
* Drops and creates a new index on a table.
*
* @param Table|string $table The name of the table on which the index is to be created.
*
* @return void
*/
public function dropAndCreateIndex(Index $index, $table)
{
$this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table);
$this->createIndex($index, $table);
}
/**
* Drops and creates a new foreign key.
*
* @param ForeignKeyConstraint $foreignKey An associative array that defines properties
* of the foreign key to be created.
* @param Table|string $table The name of the table on which the foreign key is to be created.
*
* @return void
*/
public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table)
{
$this->tryMethod('dropForeignKey', $foreignKey, $table);
$this->createForeignKey($foreignKey, $table);
}
/**
* Drops and create a new sequence.
*
* @return void
*
* @throws ConnectionException If something fails at database level.
*/
public function dropAndCreateSequence(Sequence $sequence)
{
$this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform));
$this->createSequence($sequence);
}
/**
* Drops and creates a new table.
*
* @return void
*/
public function dropAndCreateTable(Table $table)
{
$this->tryMethod('dropTable', $table->getQuotedName($this->_platform));
$this->createTable($table);
}
/**
* Drops and creates a new database.
*
* @param string $database The name of the database to create.
*
* @return void
*/
public function dropAndCreateDatabase($database)
{
$this->tryMethod('dropDatabase', $database);
$this->createDatabase($database);
}
/**
* Drops and creates a new view.
*
* @return void
*/
public function dropAndCreateView(View $view)
{
$this->tryMethod('dropView', $view->getQuotedName($this->_platform));
$this->createView($view);
}
/* alterTable() Methods */
/**
* Alters an existing tables schema.
*
* @return void
*/
public function alterTable(TableDiff $tableDiff)
{
$queries = $this->_platform->getAlterTableSQL($tableDiff);
if (! is_array($queries) || ! count($queries)) {
return;
}
foreach ($queries as $ddlQuery) {
$this->_execSql($ddlQuery);
}
}
/**
* Renames a given table to another name.
*
* @param string $name The current name of the table.
* @param string $newName The new name of the table.
*
* @return void
*/
public function renameTable($name, $newName)
{
$tableDiff = new TableDiff($name);
$tableDiff->newName = $newName;
$this->alterTable($tableDiff);
}
/**
* Methods for filtering return values of list*() methods to convert
* the native DBMS data definition to a portable Doctrine definition
*/
/**
* @param mixed[] $databases
*
* @return string[]
*/
protected function _getPortableDatabasesList($databases)
{
$list = [];
foreach ($databases as $value) {
$value = $this->_getPortableDatabaseDefinition($value);
if (! $value) {
continue;
}
$list[] = $value;
}
return $list;
}
/**
* Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition.
*
* @param mixed[][] $namespaces The list of namespace names in the native DBMS data definition.
*
* @return string[]
*/
protected function getPortableNamespacesList(array $namespaces)
{
$namespacesList = [];
foreach ($namespaces as $namespace) {
$namespacesList[] = $this->getPortableNamespaceDefinition($namespace);
}
return $namespacesList;
}
/**
* @param mixed $database
*
* @return mixed
*/
protected function _getPortableDatabaseDefinition($database)
{
return $database;
}
/**
* Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition.
*
* @param mixed[] $namespace The native DBMS namespace definition.
*
* @return mixed
*/
protected function getPortableNamespaceDefinition(array $namespace)
{
return $namespace;
}
/**
* @deprecated
*
* @param mixed[][] $functions
*
* @return mixed[][]
*/
protected function _getPortableFunctionsList($functions)
{
$list = [];
foreach ($functions as $value) {
$value = $this->_getPortableFunctionDefinition($value);
if (! $value) {
continue;
}
$list[] = $value;
}
return $list;
}
/**
* @deprecated
*
* @param mixed[] $function
*
* @return mixed
*/
protected function _getPortableFunctionDefinition($function)
{
return $function;
}
/**
* @param mixed[][] $triggers
*
* @return mixed[][]
*/
protected function _getPortableTriggersList($triggers)
{
$list = [];
foreach ($triggers as $value) {
$value = $this->_getPortableTriggerDefinition($value);
if (! $value) {
continue;
}
$list[] = $value;
}
return $list;
}
/**
* @param mixed[] $trigger
*
* @return mixed
*/
protected function _getPortableTriggerDefinition($trigger)
{
return $trigger;
}
/**
* @param mixed[][] $sequences
*
* @return Sequence[]
*/
protected function _getPortableSequencesList($sequences)
{
$list = [];
foreach ($sequences as $value) {
$list[] = $this->_getPortableSequenceDefinition($value);
}
return $list;
}
/**
* @param mixed[] $sequence
*
* @return Sequence
*
* @throws DBALException
*/
protected function _getPortableSequenceDefinition($sequence)
{
throw DBALException::notSupported('Sequences');
}
/**
* Independent of the database the keys of the column list result are lowercased.
*
* The name of the created column instance however is kept in its case.
*
* @param string $table The name of the table.
* @param string $database
* @param mixed[][] $tableColumns
*
* @return Column[]
*/
protected function _getPortableTableColumnList($table, $database, $tableColumns)
{
$eventManager = $this->_platform->getEventManager();
$list = [];
foreach ($tableColumns as $tableColumn) {
$column = null;
$defaultPrevented = false;
if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) {
$eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn);
$eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs);
$defaultPrevented = $eventArgs->isDefaultPrevented();
$column = $eventArgs->getColumn();
}
if (! $defaultPrevented) {
$column = $this->_getPortableTableColumnDefinition($tableColumn);
}
if (! $column) {
continue;
}
$name = strtolower($column->getQuotedName($this->_platform));
$list[$name] = $column;
}
return $list;
}
/**
* Gets Table Column Definition.
*
* @param mixed[] $tableColumn
*
* @return Column
*/
abstract protected function _getPortableTableColumnDefinition($tableColumn);
/**
* Aggregates and groups the index results according to the required data result.
*
* @param mixed[][] $tableIndexes
* @param string|null $tableName
*
* @return Index[]
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
$result = [];
foreach ($tableIndexes as $tableIndex) {
$indexName = $keyName = $tableIndex['key_name'];
if ($tableIndex['primary']) {
$keyName = 'primary';
}
$keyName = strtolower($keyName);
if (! isset($result[$keyName])) {
$options = [
'lengths' => [],
];
if (isset($tableIndex['where'])) {
$options['where'] = $tableIndex['where'];
}
$result[$keyName] = [
'name' => $indexName,
'columns' => [],
'unique' => ! $tableIndex['non_unique'],
'primary' => $tableIndex['primary'],
'flags' => $tableIndex['flags'] ?? [],
'options' => $options,
];
}
$result[$keyName]['columns'][] = $tableIndex['column_name'];
$result[$keyName]['options']['lengths'][] = $tableIndex['length'] ?? null;
}
$eventManager = $this->_platform->getEventManager();
$indexes = [];
foreach ($result as $indexKey => $data) {
$index = null;
$defaultPrevented = false;
if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) {
$eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn);
$eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs);
$defaultPrevented = $eventArgs->isDefaultPrevented();
$index = $eventArgs->getIndex();
}
if (! $defaultPrevented) {
$index = new Index(
$data['name'],
$data['columns'],
$data['unique'],
$data['primary'],
$data['flags'],
$data['options']
);
}
if (! $index) {
continue;
}
$indexes[$indexKey] = $index;
}
return $indexes;
}
/**
* @param mixed[][] $tables
*
* @return string[]
*/
protected function _getPortableTablesList($tables)
{
$list = [];
foreach ($tables as $value) {
$value = $this->_getPortableTableDefinition($value);
if (! $value) {
continue;
}
$list[] = $value;
}
return $list;
}
/**
* @param mixed $table
*
* @return string
*/
protected function _getPortableTableDefinition($table)
{
return $table;
}
/**
* @param mixed[][] $users
*
* @return string[][]
*/
protected function _getPortableUsersList($users)
{
$list = [];
foreach ($users as $value) {
$value = $this->_getPortableUserDefinition($value);
if (! $value) {
continue;
}
$list[] = $value;
}
return $list;
}
/**
* @param string[] $user
*
* @return string[]
*/
protected function _getPortableUserDefinition($user)
{
return $user;
}
/**
* @param mixed[][] $views
*
* @return View[]
*/
protected function _getPortableViewsList($views)
{
$list = [];
foreach ($views as $value) {
$view = $this->_getPortableViewDefinition($value);
if (! $view) {
continue;
}
$viewName = strtolower($view->getQuotedName($this->_platform));
$list[$viewName] = $view;
}
return $list;
}
/**
* @param mixed[] $view
*
* @return View|false
*/
protected function _getPortableViewDefinition($view)
{
return false;
}
/**
* @param mixed[][] $tableForeignKeys
*
* @return ForeignKeyConstraint[]
*/
protected function _getPortableTableForeignKeysList($tableForeignKeys)
{
$list = [];
foreach ($tableForeignKeys as $value) {
$list[] = $this->_getPortableTableForeignKeyDefinition($value);
}
return $list;
}
/**
* @param mixed $tableForeignKey
*
* @return ForeignKeyConstraint
*/
protected function _getPortableTableForeignKeyDefinition($tableForeignKey)
{
return $tableForeignKey;
}
/**
* @param string[]|string $sql
*
* @return void
*/
protected function _execSql($sql)
{
foreach ((array) $sql as $query) {
$this->_conn->executeUpdate($query);
}
}
/**
* Creates a schema instance for the current database.
*
* @return Schema
*/
public function createSchema()
{
$namespaces = [];
if ($this->_platform->supportsSchemas()) {
$namespaces = $this->listNamespaceNames();
}
$sequences = [];
if ($this->_platform->supportsSequences()) {
$sequences = $this->listSequences();
}
$tables = $this->listTables();
return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces);
}
/**
* Creates the configuration for this schema.
*
* @return SchemaConfig
*/
public function createSchemaConfig()
{
$schemaConfig = new SchemaConfig();
$schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength());
$searchPaths = $this->getSchemaSearchPaths();
if (isset($searchPaths[0])) {
$schemaConfig->setName($searchPaths[0]);
}
$params = $this->_conn->getParams();
if (! isset($params['defaultTableOptions'])) {
$params['defaultTableOptions'] = [];
}
if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) {
$params['defaultTableOptions']['charset'] = $params['charset'];
}
$schemaConfig->setDefaultTableOptions($params['defaultTableOptions']);
return $schemaConfig;
}
/**
* The search path for namespaces in the currently connected database.
*
* The first entry is usually the default namespace in the Schema. All
* further namespaces contain tables/sequences which can also be addressed
* with a short, not full-qualified name.
*
* For databases that don't support subschema/namespaces this method
* returns the name of the currently connected database.
*
* @return string[]
*/
public function getSchemaSearchPaths()
{
return [$this->_conn->getDatabase()];
}
/**
* Given a table comment this method tries to extract a typehint for Doctrine Type, or returns
* the type given as default.
*
* @param string|null $comment
* @param string $currentType
*
* @return string
*/
public function extractDoctrineTypeFromComment($comment, $currentType)
{
if ($comment !== null && preg_match('(\(DC2Type:(((?!\)).)+)\))', $comment, $match)) {
return $match[1];
}
return $currentType;
}
/**
* @param string|null $comment
* @param string|null $type
*
* @return string|null
*/
public function removeDoctrineTypeFromComment($comment, $type)
{
if ($comment === null) {
return null;
}
return str_replace('(DC2Type:' . $type . ')', '', $comment);
}
}
lib/Doctrine/DBAL/Schema/Column.php 0000644 00000020414 13727250467 0012747 0 ustar 00 _setName($name);
$this->setType($type);
$this->setOptions($options);
}
/**
* @param mixed[] $options
*
* @return Column
*/
public function setOptions(array $options)
{
foreach ($options as $name => $value) {
$method = 'set' . $name;
if (! method_exists($this, $method)) {
// next major: throw an exception
@trigger_error(sprintf(
'The "%s" column option is not supported,' .
' setting it is deprecated and will cause an error in Doctrine DBAL 3.0',
$name
), E_USER_DEPRECATED);
continue;
}
$this->$method($value);
}
return $this;
}
/**
* @return Column
*/
public function setType(Type $type)
{
$this->_type = $type;
return $this;
}
/**
* @param int|null $length
*
* @return Column
*/
public function setLength($length)
{
if ($length !== null) {
$this->_length = (int) $length;
} else {
$this->_length = null;
}
return $this;
}
/**
* @param int $precision
*
* @return Column
*/
public function setPrecision($precision)
{
if (! is_numeric($precision)) {
$precision = 10; // defaults to 10 when no valid precision is given.
}
$this->_precision = (int) $precision;
return $this;
}
/**
* @param int $scale
*
* @return Column
*/
public function setScale($scale)
{
if (! is_numeric($scale)) {
$scale = 0;
}
$this->_scale = (int) $scale;
return $this;
}
/**
* @param bool $unsigned
*
* @return Column
*/
public function setUnsigned($unsigned)
{
$this->_unsigned = (bool) $unsigned;
return $this;
}
/**
* @param bool $fixed
*
* @return Column
*/
public function setFixed($fixed)
{
$this->_fixed = (bool) $fixed;
return $this;
}
/**
* @param bool $notnull
*
* @return Column
*/
public function setNotnull($notnull)
{
$this->_notnull = (bool) $notnull;
return $this;
}
/**
* @param mixed $default
*
* @return Column
*/
public function setDefault($default)
{
$this->_default = $default;
return $this;
}
/**
* @param mixed[] $platformOptions
*
* @return Column
*/
public function setPlatformOptions(array $platformOptions)
{
$this->_platformOptions = $platformOptions;
return $this;
}
/**
* @param string $name
* @param mixed $value
*
* @return Column
*/
public function setPlatformOption($name, $value)
{
$this->_platformOptions[$name] = $value;
return $this;
}
/**
* @param string $value
*
* @return Column
*/
public function setColumnDefinition($value)
{
$this->_columnDefinition = $value;
return $this;
}
/**
* @return Type
*/
public function getType()
{
return $this->_type;
}
/**
* @return int|null
*/
public function getLength()
{
return $this->_length;
}
/**
* @return int
*/
public function getPrecision()
{
return $this->_precision;
}
/**
* @return int
*/
public function getScale()
{
return $this->_scale;
}
/**
* @return bool
*/
public function getUnsigned()
{
return $this->_unsigned;
}
/**
* @return bool
*/
public function getFixed()
{
return $this->_fixed;
}
/**
* @return bool
*/
public function getNotnull()
{
return $this->_notnull;
}
/**
* @return string|null
*/
public function getDefault()
{
return $this->_default;
}
/**
* @return mixed[]
*/
public function getPlatformOptions()
{
return $this->_platformOptions;
}
/**
* @param string $name
*
* @return bool
*/
public function hasPlatformOption($name)
{
return isset($this->_platformOptions[$name]);
}
/**
* @param string $name
*
* @return mixed
*/
public function getPlatformOption($name)
{
return $this->_platformOptions[$name];
}
/**
* @return string|null
*/
public function getColumnDefinition()
{
return $this->_columnDefinition;
}
/**
* @return bool
*/
public function getAutoincrement()
{
return $this->_autoincrement;
}
/**
* @param bool $flag
*
* @return Column
*/
public function setAutoincrement($flag)
{
$this->_autoincrement = $flag;
return $this;
}
/**
* @param string|null $comment
*
* @return Column
*/
public function setComment($comment)
{
$this->_comment = $comment;
return $this;
}
/**
* @return string|null
*/
public function getComment()
{
return $this->_comment;
}
/**
* @param string $name
* @param mixed $value
*
* @return Column
*/
public function setCustomSchemaOption($name, $value)
{
$this->_customSchemaOptions[$name] = $value;
return $this;
}
/**
* @param string $name
*
* @return bool
*/
public function hasCustomSchemaOption($name)
{
return isset($this->_customSchemaOptions[$name]);
}
/**
* @param string $name
*
* @return mixed
*/
public function getCustomSchemaOption($name)
{
return $this->_customSchemaOptions[$name];
}
/**
* @param mixed[] $customSchemaOptions
*
* @return Column
*/
public function setCustomSchemaOptions(array $customSchemaOptions)
{
$this->_customSchemaOptions = $customSchemaOptions;
return $this;
}
/**
* @return mixed[]
*/
public function getCustomSchemaOptions()
{
return $this->_customSchemaOptions;
}
/**
* @return mixed[]
*/
public function toArray()
{
return array_merge([
'name' => $this->_name,
'type' => $this->_type,
'default' => $this->_default,
'notnull' => $this->_notnull,
'length' => $this->_length,
'precision' => $this->_precision,
'scale' => $this->_scale,
'fixed' => $this->_fixed,
'unsigned' => $this->_unsigned,
'autoincrement' => $this->_autoincrement,
'columnDefinition' => $this->_columnDefinition,
'comment' => $this->_comment,
], $this->_platformOptions, $this->_customSchemaOptions);
}
}
lib/Doctrine/DBAL/Schema/ColumnDiff.php 0000644 00000002320 13727250467 0013534 0 ustar 00 oldColumnName = $oldColumnName;
$this->column = $column;
$this->changedProperties = $changedProperties;
$this->fromColumn = $fromColumn;
}
/**
* @param string $propertyName
*
* @return bool
*/
public function hasChanged($propertyName)
{
return in_array($propertyName, $this->changedProperties);
}
/**
* @return Identifier
*/
public function getOldColumnName()
{
$quote = $this->fromColumn && $this->fromColumn->isQuoted();
return new Identifier($this->oldColumnName, $quote);
}
}
lib/Doctrine/DBAL/Schema/Comparator.php 0000644 00000046420 13727250467 0013626 0 ustar 00 compare($fromSchema, $toSchema);
}
/**
* Returns a SchemaDiff object containing the differences between the schemas $fromSchema and $toSchema.
*
* The returned differences are returned in such a way that they contain the
* operations to change the schema stored in $fromSchema to the schema that is
* stored in $toSchema.
*
* @return SchemaDiff
*/
public function compare(Schema $fromSchema, Schema $toSchema)
{
$diff = new SchemaDiff();
$diff->fromSchema = $fromSchema;
$foreignKeysToTable = [];
foreach ($toSchema->getNamespaces() as $namespace) {
if ($fromSchema->hasNamespace($namespace)) {
continue;
}
$diff->newNamespaces[$namespace] = $namespace;
}
foreach ($fromSchema->getNamespaces() as $namespace) {
if ($toSchema->hasNamespace($namespace)) {
continue;
}
$diff->removedNamespaces[$namespace] = $namespace;
}
foreach ($toSchema->getTables() as $table) {
$tableName = $table->getShortestName($toSchema->getName());
if (! $fromSchema->hasTable($tableName)) {
$diff->newTables[$tableName] = $toSchema->getTable($tableName);
} else {
$tableDifferences = $this->diffTable(
$fromSchema->getTable($tableName),
$toSchema->getTable($tableName)
);
if ($tableDifferences !== false) {
$diff->changedTables[$tableName] = $tableDifferences;
}
}
}
/* Check if there are tables removed */
foreach ($fromSchema->getTables() as $table) {
$tableName = $table->getShortestName($fromSchema->getName());
$table = $fromSchema->getTable($tableName);
if (! $toSchema->hasTable($tableName)) {
$diff->removedTables[$tableName] = $table;
}
// also remember all foreign keys that point to a specific table
foreach ($table->getForeignKeys() as $foreignKey) {
$foreignTable = strtolower($foreignKey->getForeignTableName());
if (! isset($foreignKeysToTable[$foreignTable])) {
$foreignKeysToTable[$foreignTable] = [];
}
$foreignKeysToTable[$foreignTable][] = $foreignKey;
}
}
foreach ($diff->removedTables as $tableName => $table) {
if (! isset($foreignKeysToTable[$tableName])) {
continue;
}
$diff->orphanedForeignKeys = array_merge($diff->orphanedForeignKeys, $foreignKeysToTable[$tableName]);
// deleting duplicated foreign keys present on both on the orphanedForeignKey
// and the removedForeignKeys from changedTables
foreach ($foreignKeysToTable[$tableName] as $foreignKey) {
// strtolower the table name to make if compatible with getShortestName
$localTableName = strtolower($foreignKey->getLocalTableName());
if (! isset($diff->changedTables[$localTableName])) {
continue;
}
foreach ($diff->changedTables[$localTableName]->removedForeignKeys as $key => $removedForeignKey) {
assert($removedForeignKey instanceof ForeignKeyConstraint);
// We check if the key is from the removed table if not we skip.
if ($tableName !== strtolower($removedForeignKey->getForeignTableName())) {
continue;
}
unset($diff->changedTables[$localTableName]->removedForeignKeys[$key]);
}
}
}
foreach ($toSchema->getSequences() as $sequence) {
$sequenceName = $sequence->getShortestName($toSchema->getName());
if (! $fromSchema->hasSequence($sequenceName)) {
if (! $this->isAutoIncrementSequenceInSchema($fromSchema, $sequence)) {
$diff->newSequences[] = $sequence;
}
} else {
if ($this->diffSequence($sequence, $fromSchema->getSequence($sequenceName))) {
$diff->changedSequences[] = $toSchema->getSequence($sequenceName);
}
}
}
foreach ($fromSchema->getSequences() as $sequence) {
if ($this->isAutoIncrementSequenceInSchema($toSchema, $sequence)) {
continue;
}
$sequenceName = $sequence->getShortestName($fromSchema->getName());
if ($toSchema->hasSequence($sequenceName)) {
continue;
}
$diff->removedSequences[] = $sequence;
}
return $diff;
}
/**
* @param Schema $schema
* @param Sequence $sequence
*
* @return bool
*/
private function isAutoIncrementSequenceInSchema($schema, $sequence)
{
foreach ($schema->getTables() as $table) {
if ($sequence->isAutoIncrementsFor($table)) {
return true;
}
}
return false;
}
/**
* @return bool
*/
public function diffSequence(Sequence $sequence1, Sequence $sequence2)
{
if ($sequence1->getAllocationSize() !== $sequence2->getAllocationSize()) {
return true;
}
return $sequence1->getInitialValue() !== $sequence2->getInitialValue();
}
/**
* Returns the difference between the tables $table1 and $table2.
*
* If there are no differences this method returns the boolean false.
*
* @return TableDiff|false
*/
public function diffTable(Table $table1, Table $table2)
{
$changes = 0;
$tableDifferences = new TableDiff($table1->getName());
$tableDifferences->fromTable = $table1;
$table1Columns = $table1->getColumns();
$table2Columns = $table2->getColumns();
/* See if all the columns in table 1 exist in table 2 */
foreach ($table2Columns as $columnName => $column) {
if ($table1->hasColumn($columnName)) {
continue;
}
$tableDifferences->addedColumns[$columnName] = $column;
$changes++;
}
/* See if there are any removed columns in table 2 */
foreach ($table1Columns as $columnName => $column) {
// See if column is removed in table 2.
if (! $table2->hasColumn($columnName)) {
$tableDifferences->removedColumns[$columnName] = $column;
$changes++;
continue;
}
// See if column has changed properties in table 2.
$changedProperties = $this->diffColumn($column, $table2->getColumn($columnName));
if (empty($changedProperties)) {
continue;
}
$columnDiff = new ColumnDiff($column->getName(), $table2->getColumn($columnName), $changedProperties);
$columnDiff->fromColumn = $column;
$tableDifferences->changedColumns[$column->getName()] = $columnDiff;
$changes++;
}
$this->detectColumnRenamings($tableDifferences);
$table1Indexes = $table1->getIndexes();
$table2Indexes = $table2->getIndexes();
/* See if all the indexes in table 1 exist in table 2 */
foreach ($table2Indexes as $indexName => $index) {
if (($index->isPrimary() && $table1->hasPrimaryKey()) || $table1->hasIndex($indexName)) {
continue;
}
$tableDifferences->addedIndexes[$indexName] = $index;
$changes++;
}
/* See if there are any removed indexes in table 2 */
foreach ($table1Indexes as $indexName => $index) {
// See if index is removed in table 2.
if (
($index->isPrimary() && ! $table2->hasPrimaryKey()) ||
! $index->isPrimary() && ! $table2->hasIndex($indexName)
) {
$tableDifferences->removedIndexes[$indexName] = $index;
$changes++;
continue;
}
// See if index has changed in table 2.
$table2Index = $index->isPrimary() ? $table2->getPrimaryKey() : $table2->getIndex($indexName);
assert($table2Index instanceof Index);
if (! $this->diffIndex($index, $table2Index)) {
continue;
}
$tableDifferences->changedIndexes[$indexName] = $table2Index;
$changes++;
}
$this->detectIndexRenamings($tableDifferences);
$fromFkeys = $table1->getForeignKeys();
$toFkeys = $table2->getForeignKeys();
foreach ($fromFkeys as $key1 => $constraint1) {
foreach ($toFkeys as $key2 => $constraint2) {
if ($this->diffForeignKey($constraint1, $constraint2) === false) {
unset($fromFkeys[$key1], $toFkeys[$key2]);
} else {
if (strtolower($constraint1->getName()) === strtolower($constraint2->getName())) {
$tableDifferences->changedForeignKeys[] = $constraint2;
$changes++;
unset($fromFkeys[$key1], $toFkeys[$key2]);
}
}
}
}
foreach ($fromFkeys as $constraint1) {
$tableDifferences->removedForeignKeys[] = $constraint1;
$changes++;
}
foreach ($toFkeys as $constraint2) {
$tableDifferences->addedForeignKeys[] = $constraint2;
$changes++;
}
return $changes ? $tableDifferences : false;
}
/**
* Try to find columns that only changed their name, rename operations maybe cheaper than add/drop
* however ambiguities between different possibilities should not lead to renaming at all.
*
* @return void
*/
private function detectColumnRenamings(TableDiff $tableDifferences)
{
$renameCandidates = [];
foreach ($tableDifferences->addedColumns as $addedColumnName => $addedColumn) {
foreach ($tableDifferences->removedColumns as $removedColumn) {
if (count($this->diffColumn($addedColumn, $removedColumn)) !== 0) {
continue;
}
$renameCandidates[$addedColumn->getName()][] = [$removedColumn, $addedColumn, $addedColumnName];
}
}
foreach ($renameCandidates as $candidateColumns) {
if (count($candidateColumns) !== 1) {
continue;
}
[$removedColumn, $addedColumn] = $candidateColumns[0];
$removedColumnName = strtolower($removedColumn->getName());
$addedColumnName = strtolower($addedColumn->getName());
if (isset($tableDifferences->renamedColumns[$removedColumnName])) {
continue;
}
$tableDifferences->renamedColumns[$removedColumnName] = $addedColumn;
unset(
$tableDifferences->addedColumns[$addedColumnName],
$tableDifferences->removedColumns[$removedColumnName]
);
}
}
/**
* Try to find indexes that only changed their name, rename operations maybe cheaper than add/drop
* however ambiguities between different possibilities should not lead to renaming at all.
*
* @return void
*/
private function detectIndexRenamings(TableDiff $tableDifferences)
{
$renameCandidates = [];
// Gather possible rename candidates by comparing each added and removed index based on semantics.
foreach ($tableDifferences->addedIndexes as $addedIndexName => $addedIndex) {
foreach ($tableDifferences->removedIndexes as $removedIndex) {
if ($this->diffIndex($addedIndex, $removedIndex)) {
continue;
}
$renameCandidates[$addedIndex->getName()][] = [$removedIndex, $addedIndex, $addedIndexName];
}
}
foreach ($renameCandidates as $candidateIndexes) {
// If the current rename candidate contains exactly one semantically equal index,
// we can safely rename it.
// Otherwise it is unclear if a rename action is really intended,
// therefore we let those ambiguous indexes be added/dropped.
if (count($candidateIndexes) !== 1) {
continue;
}
[$removedIndex, $addedIndex] = $candidateIndexes[0];
$removedIndexName = strtolower($removedIndex->getName());
$addedIndexName = strtolower($addedIndex->getName());
if (isset($tableDifferences->renamedIndexes[$removedIndexName])) {
continue;
}
$tableDifferences->renamedIndexes[$removedIndexName] = $addedIndex;
unset(
$tableDifferences->addedIndexes[$addedIndexName],
$tableDifferences->removedIndexes[$removedIndexName]
);
}
}
/**
* @return bool
*/
public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2)
{
if (
array_map('strtolower', $key1->getUnquotedLocalColumns())
!== array_map('strtolower', $key2->getUnquotedLocalColumns())
) {
return true;
}
if (
array_map('strtolower', $key1->getUnquotedForeignColumns())
!== array_map('strtolower', $key2->getUnquotedForeignColumns())
) {
return true;
}
if ($key1->getUnqualifiedForeignTableName() !== $key2->getUnqualifiedForeignTableName()) {
return true;
}
if ($key1->onUpdate() !== $key2->onUpdate()) {
return true;
}
return $key1->onDelete() !== $key2->onDelete();
}
/**
* Returns the difference between the columns
*
* If there are differences this method returns $field2, otherwise the
* boolean false.
*
* @return string[]
*/
public function diffColumn(Column $column1, Column $column2)
{
$properties1 = $column1->toArray();
$properties2 = $column2->toArray();
$changedProperties = [];
if (get_class($properties1['type']) !== get_class($properties2['type'])) {
$changedProperties[] = 'type';
}
foreach (['notnull', 'unsigned', 'autoincrement'] as $property) {
if ($properties1[$property] === $properties2[$property]) {
continue;
}
$changedProperties[] = $property;
}
// This is a very nasty hack to make comparator work with the legacy json_array type,
// which should be killed in v3
if ($this->isALegacyJsonComparison($properties1['type'], $properties2['type'])) {
array_shift($changedProperties);
$changedProperties[] = 'comment';
}
// Null values need to be checked additionally as they tell whether to create or drop a default value.
// null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation.
if (
($properties1['default'] === null) !== ($properties2['default'] === null)
|| $properties1['default'] != $properties2['default']
) {
$changedProperties[] = 'default';
}
if (
($properties1['type'] instanceof Types\StringType && ! $properties1['type'] instanceof Types\GuidType) ||
$properties1['type'] instanceof Types\BinaryType
) {
// check if value of length is set at all, default value assumed otherwise.
$length1 = $properties1['length'] ?: 255;
$length2 = $properties2['length'] ?: 255;
if ($length1 !== $length2) {
$changedProperties[] = 'length';
}
if ($properties1['fixed'] !== $properties2['fixed']) {
$changedProperties[] = 'fixed';
}
} elseif ($properties1['type'] instanceof Types\DecimalType) {
if (($properties1['precision'] ?: 10) !== ($properties2['precision'] ?: 10)) {
$changedProperties[] = 'precision';
}
if ($properties1['scale'] !== $properties2['scale']) {
$changedProperties[] = 'scale';
}
}
// A null value and an empty string are actually equal for a comment so they should not trigger a change.
if (
$properties1['comment'] !== $properties2['comment'] &&
! ($properties1['comment'] === null && $properties2['comment'] === '') &&
! ($properties2['comment'] === null && $properties1['comment'] === '')
) {
$changedProperties[] = 'comment';
}
$customOptions1 = $column1->getCustomSchemaOptions();
$customOptions2 = $column2->getCustomSchemaOptions();
foreach (array_merge(array_keys($customOptions1), array_keys($customOptions2)) as $key) {
if (! array_key_exists($key, $properties1) || ! array_key_exists($key, $properties2)) {
$changedProperties[] = $key;
} elseif ($properties1[$key] !== $properties2[$key]) {
$changedProperties[] = $key;
}
}
$platformOptions1 = $column1->getPlatformOptions();
$platformOptions2 = $column2->getPlatformOptions();
foreach (array_keys(array_intersect_key($platformOptions1, $platformOptions2)) as $key) {
if ($properties1[$key] === $properties2[$key]) {
continue;
}
$changedProperties[] = $key;
}
return array_unique($changedProperties);
}
/**
* TODO: kill with fire on v3.0
*
* @deprecated
*/
private function isALegacyJsonComparison(Types\Type $one, Types\Type $other): bool
{
if (! $one instanceof Types\JsonType || ! $other instanceof Types\JsonType) {
return false;
}
return (! $one instanceof Types\JsonArrayType && $other instanceof Types\JsonArrayType)
|| (! $other instanceof Types\JsonArrayType && $one instanceof Types\JsonArrayType);
}
/**
* Finds the difference between the indexes $index1 and $index2.
*
* Compares $index1 with $index2 and returns $index2 if there are any
* differences or false in case there are no differences.
*
* @return bool
*/
public function diffIndex(Index $index1, Index $index2)
{
return ! ($index1->isFullfilledBy($index2) && $index2->isFullfilledBy($index1));
}
}
lib/Doctrine/DBAL/Schema/Constraint.php 0000644 00000001741 13727250467 0013640 0 ustar 00 _platform->getListTablesSQL();
$sql .= ' AND CREATOR = UPPER(' . $this->_conn->quote($this->_conn->getUsername()) . ')';
$tables = $this->_conn->fetchAll($sql);
return $this->filterAssetNames($this->_getPortableTablesList($tables));
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableColumnDefinition($tableColumn)
{
$tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
$length = null;
$fixed = null;
$scale = false;
$precision = false;
$default = null;
if ($tableColumn['default'] !== null && $tableColumn['default'] !== 'NULL') {
$default = $tableColumn['default'];
if (preg_match('/^\'(.*)\'$/s', $default, $matches)) {
$default = str_replace("''", "'", $matches[1]);
}
}
$type = $this->_platform->getDoctrineTypeMapping($tableColumn['typename']);
if (isset($tableColumn['comment'])) {
$type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
$tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
}
switch (strtolower($tableColumn['typename'])) {
case 'varchar':
$length = $tableColumn['length'];
$fixed = false;
break;
case 'character':
$length = $tableColumn['length'];
$fixed = true;
break;
case 'clob':
$length = $tableColumn['length'];
break;
case 'decimal':
case 'double':
case 'real':
$scale = $tableColumn['scale'];
$precision = $tableColumn['length'];
break;
}
$options = [
'length' => $length,
'unsigned' => false,
'fixed' => (bool) $fixed,
'default' => $default,
'autoincrement' => (bool) $tableColumn['autoincrement'],
'notnull' => (bool) ($tableColumn['nulls'] === 'N'),
'scale' => null,
'precision' => null,
'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
? $tableColumn['comment']
: null,
'platformOptions' => [],
];
if ($scale !== null && $precision !== null) {
$options['scale'] = $scale;
$options['precision'] = $precision;
}
return new Column($tableColumn['colname'], Type::getType($type), $options);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTablesList($tables)
{
$tableNames = [];
foreach ($tables as $tableRow) {
$tableRow = array_change_key_case($tableRow, CASE_LOWER);
$tableNames[] = $tableRow['name'];
}
return $tableNames;
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
foreach ($tableIndexes as &$tableIndexRow) {
$tableIndexRow = array_change_key_case($tableIndexRow, CASE_LOWER);
$tableIndexRow['primary'] = (bool) $tableIndexRow['primary'];
}
return parent::_getPortableTableIndexesList($tableIndexes, $tableName);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableForeignKeyDefinition($tableForeignKey)
{
return new ForeignKeyConstraint(
$tableForeignKey['local_columns'],
$tableForeignKey['foreign_table'],
$tableForeignKey['foreign_columns'],
$tableForeignKey['name'],
$tableForeignKey['options']
);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableForeignKeysList($tableForeignKeys)
{
$foreignKeys = [];
foreach ($tableForeignKeys as $tableForeignKey) {
$tableForeignKey = array_change_key_case($tableForeignKey, CASE_LOWER);
if (! isset($foreignKeys[$tableForeignKey['index_name']])) {
$foreignKeys[$tableForeignKey['index_name']] = [
'local_columns' => [$tableForeignKey['local_column']],
'foreign_table' => $tableForeignKey['foreign_table'],
'foreign_columns' => [$tableForeignKey['foreign_column']],
'name' => $tableForeignKey['index_name'],
'options' => [
'onUpdate' => $tableForeignKey['on_update'],
'onDelete' => $tableForeignKey['on_delete'],
],
];
} else {
$foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column'];
$foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column'];
}
}
return parent::_getPortableTableForeignKeysList($foreignKeys);
}
/**
* @param string $def
*
* @return string|null
*/
protected function _getPortableForeignKeyRuleDef($def)
{
if ($def === 'C') {
return 'CASCADE';
}
if ($def === 'N') {
return 'SET NULL';
}
return null;
}
/**
* {@inheritdoc}
*/
protected function _getPortableViewDefinition($view)
{
$view = array_change_key_case($view, CASE_LOWER);
// sadly this still segfaults on PDO_IBM, see http://pecl.php.net/bugs/bug.php?id=17199
//$view['text'] = (is_resource($view['text']) ? stream_get_contents($view['text']) : $view['text']);
if (! is_resource($view['text'])) {
$pos = strpos($view['text'], ' AS ');
$sql = substr($view['text'], $pos + 4);
} else {
$sql = '';
}
return new View($view['name'], $sql);
}
/**
* {@inheritdoc}
*/
public function listTableDetails($name): Table
{
$table = parent::listTableDetails($name);
$platform = $this->_platform;
assert($platform instanceof DB2Platform);
$sql = $platform->getListTableCommentsSQL($name);
$tableOptions = $this->_conn->fetchAssoc($sql);
if ($tableOptions !== false) {
$table->addOption('comment', $tableOptions['REMARKS']);
}
return $table;
}
}
lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php 0000644 00000006062 13727250467 0015554 0 ustar 00 _platform->getDoctrineTypeMapping($dbType);
$type = $this->extractDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type);
$tableColumn['COLUMN_COMMENT'] = $this->removeDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type);
$options = [
'notnull' => ! (bool) $tableColumn['IS_NULLABLE'],
'length' => (int) $tableColumn['CHARACTER_MAXIMUM_LENGTH'],
'default' => $tableColumn['COLUMN_DEFAULT'] ?? null,
'autoincrement' => (bool) $tableColumn['IS_AUTO_INCREMENT'],
'scale' => (int) $tableColumn['NUMERIC_SCALE'],
'precision' => (int) $tableColumn['NUMERIC_PRECISION'],
'comment' => isset($tableColumn['COLUMN_COMMENT']) && $tableColumn['COLUMN_COMMENT'] !== ''
? $tableColumn['COLUMN_COMMENT']
: null,
];
$column = new Column($tableColumn['COLUMN_NAME'], Type::getType($type), $options);
if (! empty($tableColumn['COLLATION_NAME'])) {
$column->setPlatformOption('collation', $tableColumn['COLLATION_NAME']);
}
return $column;
}
/**
* {@inheritdoc}
*/
protected function _getPortableDatabaseDefinition($database)
{
return $database['SCHEMA_NAME'];
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableDefinition($table)
{
return $table['TABLE_NAME'];
}
/**
* {@inheritdoc}
*/
public function _getPortableTableForeignKeyDefinition($tableForeignKey)
{
$columns = [];
foreach (explode(',', $tableForeignKey['CONSTRAINT_COLUMNS']) as $value) {
$columns[] = trim($value, ' `');
}
$refColumns = [];
foreach (explode(',', $tableForeignKey['REFERENCED_TABLE_COLUMNS']) as $value) {
$refColumns[] = trim($value, ' `');
}
return new ForeignKeyConstraint(
$columns,
$tableForeignKey['REFERENCED_TABLE_NAME'],
$refColumns,
$tableForeignKey['CONSTRAINT_NAME'],
[
'onUpdate' => $tableForeignKey['UPDATE_RULE'],
'onDelete' => $tableForeignKey['DELETE_RULE'],
]
);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
$indexes = [];
foreach ($tableIndexes as $k) {
$k['primary'] = (bool) $k['primary'];
$indexes[] = $k;
}
return parent::_getPortableTableIndexesList($indexes, $tableName);
}
}
lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php 0000644 00000025116 13727250467 0015625 0 ustar 00 Identifier)
*
* @var Identifier[]
*/
protected $_localColumnNames;
/**
* Table or asset identifier instance of the referenced table name the foreign key constraint is associated with.
*
* @var Table|Identifier
*/
protected $_foreignTableName;
/**
* Asset identifier instances of the referenced table column names the foreign key constraint is associated with.
* array($columnName => Identifier)
*
* @var Identifier[]
*/
protected $_foreignColumnNames;
/**
* Options associated with the foreign key constraint.
*
* @var mixed[]
*/
protected $_options;
/**
* Initializes the foreign key constraint.
*
* @param string[] $localColumnNames Names of the referencing table columns.
* @param Table|string $foreignTableName Referenced table.
* @param string[] $foreignColumnNames Names of the referenced table columns.
* @param string|null $name Name of the foreign key constraint.
* @param mixed[] $options Options associated with the foreign key constraint.
*/
public function __construct(
array $localColumnNames,
$foreignTableName,
array $foreignColumnNames,
$name = null,
array $options = []
) {
if ($name !== null) {
$this->_setName($name);
}
$this->_localColumnNames = $this->createIdentifierMap($localColumnNames);
if ($foreignTableName instanceof Table) {
$this->_foreignTableName = $foreignTableName;
} else {
$this->_foreignTableName = new Identifier($foreignTableName);
}
$this->_foreignColumnNames = $this->createIdentifierMap($foreignColumnNames);
$this->_options = $options;
}
/**
* @param string[] $names
*
* @return Identifier[]
*/
private function createIdentifierMap(array $names): array
{
$identifiers = [];
foreach ($names as $name) {
$identifiers[$name] = new Identifier($name);
}
return $identifiers;
}
/**
* Returns the name of the referencing table
* the foreign key constraint is associated with.
*
* @return string
*/
public function getLocalTableName()
{
return $this->_localTable->getName();
}
/**
* Sets the Table instance of the referencing table
* the foreign key constraint is associated with.
*
* @param Table $table Instance of the referencing table.
*
* @return void
*/
public function setLocalTable(Table $table)
{
$this->_localTable = $table;
}
/**
* @return Table
*/
public function getLocalTable()
{
return $this->_localTable;
}
/**
* Returns the names of the referencing table columns
* the foreign key constraint is associated with.
*
* @return string[]
*/
public function getLocalColumns()
{
return array_keys($this->_localColumnNames);
}
/**
* Returns the quoted representation of the referencing table column names
* the foreign key constraint is associated with.
*
* But only if they were defined with one or the referencing table column name
* is a keyword reserved by the platform.
* Otherwise the plain unquoted value as inserted is returned.
*
* @param AbstractPlatform $platform The platform to use for quotation.
*
* @return string[]
*/
public function getQuotedLocalColumns(AbstractPlatform $platform)
{
$columns = [];
foreach ($this->_localColumnNames as $column) {
$columns[] = $column->getQuotedName($platform);
}
return $columns;
}
/**
* Returns unquoted representation of local table column names for comparison with other FK
*
* @return string[]
*/
public function getUnquotedLocalColumns()
{
return array_map([$this, 'trimQuotes'], $this->getLocalColumns());
}
/**
* Returns unquoted representation of foreign table column names for comparison with other FK
*
* @return string[]
*/
public function getUnquotedForeignColumns()
{
return array_map([$this, 'trimQuotes'], $this->getForeignColumns());
}
/**
* {@inheritdoc}
*
* @see getLocalColumns
*/
public function getColumns()
{
return $this->getLocalColumns();
}
/**
* Returns the quoted representation of the referencing table column names
* the foreign key constraint is associated with.
*
* But only if they were defined with one or the referencing table column name
* is a keyword reserved by the platform.
* Otherwise the plain unquoted value as inserted is returned.
*
* @see getQuotedLocalColumns
*
* @param AbstractPlatform $platform The platform to use for quotation.
*
* @return string[]
*/
public function getQuotedColumns(AbstractPlatform $platform)
{
return $this->getQuotedLocalColumns($platform);
}
/**
* Returns the name of the referenced table
* the foreign key constraint is associated with.
*
* @return string
*/
public function getForeignTableName()
{
return $this->_foreignTableName->getName();
}
/**
* Returns the non-schema qualified foreign table name.
*
* @return string
*/
public function getUnqualifiedForeignTableName()
{
$name = $this->_foreignTableName->getName();
$position = strrpos($name, '.');
if ($position !== false) {
$name = substr($name, $position + 1);
}
return strtolower($name);
}
/**
* Returns the quoted representation of the referenced table name
* the foreign key constraint is associated with.
*
* But only if it was defined with one or the referenced table name
* is a keyword reserved by the platform.
* Otherwise the plain unquoted value as inserted is returned.
*
* @param AbstractPlatform $platform The platform to use for quotation.
*
* @return string
*/
public function getQuotedForeignTableName(AbstractPlatform $platform)
{
return $this->_foreignTableName->getQuotedName($platform);
}
/**
* Returns the names of the referenced table columns
* the foreign key constraint is associated with.
*
* @return string[]
*/
public function getForeignColumns()
{
return array_keys($this->_foreignColumnNames);
}
/**
* Returns the quoted representation of the referenced table column names
* the foreign key constraint is associated with.
*
* But only if they were defined with one or the referenced table column name
* is a keyword reserved by the platform.
* Otherwise the plain unquoted value as inserted is returned.
*
* @param AbstractPlatform $platform The platform to use for quotation.
*
* @return string[]
*/
public function getQuotedForeignColumns(AbstractPlatform $platform)
{
$columns = [];
foreach ($this->_foreignColumnNames as $column) {
$columns[] = $column->getQuotedName($platform);
}
return $columns;
}
/**
* Returns whether or not a given option
* is associated with the foreign key constraint.
*
* @param string $name Name of the option to check.
*
* @return bool
*/
public function hasOption($name)
{
return isset($this->_options[$name]);
}
/**
* Returns an option associated with the foreign key constraint.
*
* @param string $name Name of the option the foreign key constraint is associated with.
*
* @return mixed
*/
public function getOption($name)
{
return $this->_options[$name];
}
/**
* Returns the options associated with the foreign key constraint.
*
* @return mixed[]
*/
public function getOptions()
{
return $this->_options;
}
/**
* Returns the referential action for UPDATE operations
* on the referenced table the foreign key constraint is associated with.
*
* @return string|null
*/
public function onUpdate()
{
return $this->onEvent('onUpdate');
}
/**
* Returns the referential action for DELETE operations
* on the referenced table the foreign key constraint is associated with.
*
* @return string|null
*/
public function onDelete()
{
return $this->onEvent('onDelete');
}
/**
* Returns the referential action for a given database operation
* on the referenced table the foreign key constraint is associated with.
*
* @param string $event Name of the database operation/event to return the referential action for.
*
* @return string|null
*/
private function onEvent($event)
{
if (isset($this->_options[$event])) {
$onEvent = strtoupper($this->_options[$event]);
if (! in_array($onEvent, ['NO ACTION', 'RESTRICT'])) {
return $onEvent;
}
}
return null;
}
/**
* Checks whether this foreign key constraint intersects the given index columns.
*
* Returns `true` if at least one of this foreign key's local columns
* matches one of the given index's columns, `false` otherwise.
*
* @param Index $index The index to be checked against.
*
* @return bool
*/
public function intersectsIndexColumns(Index $index)
{
foreach ($index->getColumns() as $indexColumn) {
foreach ($this->_localColumnNames as $localColumn) {
if (strtolower($indexColumn) === strtolower($localColumn->getName())) {
return true;
}
}
}
return false;
}
}
lib/Doctrine/DBAL/Schema/Identifier.php 0000644 00000001232 13727250467 0013571 0 ustar 00 _setName($identifier);
if (! $quote || $this->_quoted) {
return;
}
$this->_setName('"' . $this->getName() . '"');
}
}
lib/Doctrine/DBAL/Schema/Index.php 0000644 00000021367 13727250467 0012571 0 ustar 00 Identifier)
*
* @var Identifier[]
*/
protected $_columns = [];
/** @var bool */
protected $_isUnique = false;
/** @var bool */
protected $_isPrimary = false;
/**
* Platform specific flags for indexes.
* array($flagName => true)
*
* @var true[]
*/
protected $_flags = [];
/**
* Platform specific options
*
* @todo $_flags should eventually be refactored into options
* @var mixed[]
*/
private $options = [];
/**
* @param string $name
* @param string[] $columns
* @param bool $isUnique
* @param bool $isPrimary
* @param string[] $flags
* @param mixed[] $options
*/
public function __construct(
$name,
array $columns,
$isUnique = false,
$isPrimary = false,
array $flags = [],
array $options = []
) {
$isUnique = $isUnique || $isPrimary;
$this->_setName($name);
$this->_isUnique = $isUnique;
$this->_isPrimary = $isPrimary;
$this->options = $options;
foreach ($columns as $column) {
$this->_addColumn($column);
}
foreach ($flags as $flag) {
$this->addFlag($flag);
}
}
/**
* @param string $column
*
* @return void
*
* @throws InvalidArgumentException
*/
protected function _addColumn($column)
{
if (! is_string($column)) {
throw new InvalidArgumentException('Expecting a string as Index Column');
}
$this->_columns[$column] = new Identifier($column);
}
/**
* {@inheritdoc}
*/
public function getColumns()
{
return array_keys($this->_columns);
}
/**
* {@inheritdoc}
*/
public function getQuotedColumns(AbstractPlatform $platform)
{
$subParts = $platform->supportsColumnLengthIndexes() && $this->hasOption('lengths')
? $this->getOption('lengths') : [];
$columns = [];
foreach ($this->_columns as $column) {
$length = array_shift($subParts);
$quotedColumn = $column->getQuotedName($platform);
if ($length !== null) {
$quotedColumn .= '(' . $length . ')';
}
$columns[] = $quotedColumn;
}
return $columns;
}
/**
* @return string[]
*/
public function getUnquotedColumns()
{
return array_map([$this, 'trimQuotes'], $this->getColumns());
}
/**
* Is the index neither unique nor primary key?
*
* @return bool
*/
public function isSimpleIndex()
{
return ! $this->_isPrimary && ! $this->_isUnique;
}
/**
* @return bool
*/
public function isUnique()
{
return $this->_isUnique;
}
/**
* @return bool
*/
public function isPrimary()
{
return $this->_isPrimary;
}
/**
* @param string $name
* @param int $pos
*
* @return bool
*/
public function hasColumnAtPosition($name, $pos = 0)
{
$name = $this->trimQuotes(strtolower($name));
$indexColumns = array_map('strtolower', $this->getUnquotedColumns());
return array_search($name, $indexColumns) === $pos;
}
/**
* Checks if this index exactly spans the given column names in the correct order.
*
* @param string[] $columnNames
*
* @return bool
*/
public function spansColumns(array $columnNames)
{
$columns = $this->getColumns();
$numberOfColumns = count($columns);
$sameColumns = true;
for ($i = 0; $i < $numberOfColumns; $i++) {
if (
isset($columnNames[$i])
&& $this->trimQuotes(strtolower($columns[$i])) === $this->trimQuotes(strtolower($columnNames[$i]))
) {
continue;
}
$sameColumns = false;
}
return $sameColumns;
}
/**
* Checks if the other index already fulfills all the indexing and constraint needs of the current one.
*
* @return bool
*/
public function isFullfilledBy(Index $other)
{
// allow the other index to be equally large only. It being larger is an option
// but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo)
if (count($other->getColumns()) !== count($this->getColumns())) {
return false;
}
// Check if columns are the same, and even in the same order
$sameColumns = $this->spansColumns($other->getColumns());
if ($sameColumns) {
if (! $this->samePartialIndex($other)) {
return false;
}
if (! $this->hasSameColumnLengths($other)) {
return false;
}
if (! $this->isUnique() && ! $this->isPrimary()) {
// this is a special case: If the current key is neither primary or unique, any unique or
// primary key will always have the same effect for the index and there cannot be any constraint
// overlaps. This means a primary or unique index can always fulfill the requirements of just an
// index that has no constraints.
return true;
}
if ($other->isPrimary() !== $this->isPrimary()) {
return false;
}
return $other->isUnique() === $this->isUnique();
}
return false;
}
/**
* Detects if the other index is a non-unique, non primary index that can be overwritten by this one.
*
* @return bool
*/
public function overrules(Index $other)
{
if ($other->isPrimary()) {
return false;
}
if ($this->isSimpleIndex() && $other->isUnique()) {
return false;
}
return $this->spansColumns($other->getColumns())
&& ($this->isPrimary() || $this->isUnique())
&& $this->samePartialIndex($other);
}
/**
* Returns platform specific flags for indexes.
*
* @return string[]
*/
public function getFlags()
{
return array_keys($this->_flags);
}
/**
* Adds Flag for an index that translates to platform specific handling.
*
* @param string $flag
*
* @return Index
*
* @example $index->addFlag('CLUSTERED')
*/
public function addFlag($flag)
{
$this->_flags[strtolower($flag)] = true;
return $this;
}
/**
* Does this index have a specific flag?
*
* @param string $flag
*
* @return bool
*/
public function hasFlag($flag)
{
return isset($this->_flags[strtolower($flag)]);
}
/**
* Removes a flag.
*
* @param string $flag
*
* @return void
*/
public function removeFlag($flag)
{
unset($this->_flags[strtolower($flag)]);
}
/**
* @param string $name
*
* @return bool
*/
public function hasOption($name)
{
return isset($this->options[strtolower($name)]);
}
/**
* @param string $name
*
* @return mixed
*/
public function getOption($name)
{
return $this->options[strtolower($name)];
}
/**
* @return mixed[]
*/
public function getOptions()
{
return $this->options;
}
/**
* Return whether the two indexes have the same partial index
*
* @return bool
*/
private function samePartialIndex(Index $other)
{
if (
$this->hasOption('where')
&& $other->hasOption('where')
&& $this->getOption('where') === $other->getOption('where')
) {
return true;
}
return ! $this->hasOption('where') && ! $other->hasOption('where');
}
/**
* Returns whether the index has the same column lengths as the other
*/
private function hasSameColumnLengths(self $other): bool
{
$filter = static function (?int $length): bool {
return $length !== null;
};
return array_filter($this->options['lengths'] ?? [], $filter)
=== array_filter($other->options['lengths'] ?? [], $filter);
}
}
lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php 0000644 00000026142 13727250467 0015177 0 ustar 00 "\0",
"\\'" => "'",
'\\"' => '"',
'\\b' => "\b",
'\\n' => "\n",
'\\r' => "\r",
'\\t' => "\t",
'\\Z' => "\x1a",
'\\\\' => '\\',
'\\%' => '%',
'\\_' => '_',
// Internally, MariaDB escapes single quotes using the standard syntax
"''" => "'",
];
/**
* {@inheritdoc}
*/
protected function _getPortableViewDefinition($view)
{
return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableDefinition($table)
{
return array_shift($table);
}
/**
* {@inheritdoc}
*/
protected function _getPortableUserDefinition($user)
{
return [
'user' => $user['User'],
'password' => $user['Password'],
];
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
foreach ($tableIndexes as $k => $v) {
$v = array_change_key_case($v, CASE_LOWER);
if ($v['key_name'] === 'PRIMARY') {
$v['primary'] = true;
} else {
$v['primary'] = false;
}
if (strpos($v['index_type'], 'FULLTEXT') !== false) {
$v['flags'] = ['FULLTEXT'];
} elseif (strpos($v['index_type'], 'SPATIAL') !== false) {
$v['flags'] = ['SPATIAL'];
}
$v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null;
$tableIndexes[$k] = $v;
}
return parent::_getPortableTableIndexesList($tableIndexes, $tableName);
}
/**
* {@inheritdoc}
*/
protected function _getPortableDatabaseDefinition($database)
{
return $database['Database'];
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableColumnDefinition($tableColumn)
{
$tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
$dbType = strtolower($tableColumn['type']);
$dbType = strtok($dbType, '(), ');
assert(is_string($dbType));
$length = $tableColumn['length'] ?? strtok('(), ');
$fixed = null;
if (! isset($tableColumn['name'])) {
$tableColumn['name'] = '';
}
$scale = null;
$precision = null;
$type = $this->_platform->getDoctrineTypeMapping($dbType);
// In cases where not connected to a database DESCRIBE $table does not return 'Comment'
if (isset($tableColumn['comment'])) {
$type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
$tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
}
switch ($dbType) {
case 'char':
case 'binary':
$fixed = true;
break;
case 'float':
case 'double':
case 'real':
case 'numeric':
case 'decimal':
if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['type'], $match)) {
$precision = $match[1];
$scale = $match[2];
$length = null;
}
break;
case 'tinytext':
$length = MySqlPlatform::LENGTH_LIMIT_TINYTEXT;
break;
case 'text':
$length = MySqlPlatform::LENGTH_LIMIT_TEXT;
break;
case 'mediumtext':
$length = MySqlPlatform::LENGTH_LIMIT_MEDIUMTEXT;
break;
case 'tinyblob':
$length = MySqlPlatform::LENGTH_LIMIT_TINYBLOB;
break;
case 'blob':
$length = MySqlPlatform::LENGTH_LIMIT_BLOB;
break;
case 'mediumblob':
$length = MySqlPlatform::LENGTH_LIMIT_MEDIUMBLOB;
break;
case 'tinyint':
case 'smallint':
case 'mediumint':
case 'int':
case 'integer':
case 'bigint':
case 'year':
$length = null;
break;
}
if ($this->_platform instanceof MariaDb1027Platform) {
$columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']);
} else {
$columnDefault = $tableColumn['default'];
}
$options = [
'length' => $length !== null ? (int) $length : null,
'unsigned' => strpos($tableColumn['type'], 'unsigned') !== false,
'fixed' => (bool) $fixed,
'default' => $columnDefault,
'notnull' => $tableColumn['null'] !== 'YES',
'scale' => null,
'precision' => null,
'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false,
'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
? $tableColumn['comment']
: null,
];
if ($scale !== null && $precision !== null) {
$options['scale'] = (int) $scale;
$options['precision'] = (int) $precision;
}
$column = new Column($tableColumn['field'], Type::getType($type), $options);
if (isset($tableColumn['characterset'])) {
$column->setPlatformOption('charset', $tableColumn['characterset']);
}
if (isset($tableColumn['collation'])) {
$column->setPlatformOption('collation', $tableColumn['collation']);
}
return $column;
}
/**
* Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers.
*
* - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted
* to distinguish them from expressions (see MDEV-10134).
* - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema
* as current_timestamp(), currdate(), currtime()
* - Quoted 'NULL' is not enforced by Maria, it is technically possible to have
* null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053)
* - \' is always stored as '' in information_schema (normalized)
*
* @link https://mariadb.com/kb/en/library/information-schema-columns-table/
* @link https://jira.mariadb.org/browse/MDEV-13132
*
* @param string|null $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7
*/
private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault): ?string
{
if ($columnDefault === 'NULL' || $columnDefault === null) {
return null;
}
if (preg_match('/^\'(.*)\'$/', $columnDefault, $matches)) {
return strtr($matches[1], self::MARIADB_ESCAPE_SEQUENCES);
}
switch ($columnDefault) {
case 'current_timestamp()':
return $platform->getCurrentTimestampSQL();
case 'curdate()':
return $platform->getCurrentDateSQL();
case 'curtime()':
return $platform->getCurrentTimeSQL();
}
return $columnDefault;
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableForeignKeysList($tableForeignKeys)
{
$list = [];
foreach ($tableForeignKeys as $value) {
$value = array_change_key_case($value, CASE_LOWER);
if (! isset($list[$value['constraint_name']])) {
if (! isset($value['delete_rule']) || $value['delete_rule'] === 'RESTRICT') {
$value['delete_rule'] = null;
}
if (! isset($value['update_rule']) || $value['update_rule'] === 'RESTRICT') {
$value['update_rule'] = null;
}
$list[$value['constraint_name']] = [
'name' => $value['constraint_name'],
'local' => [],
'foreign' => [],
'foreignTable' => $value['referenced_table_name'],
'onDelete' => $value['delete_rule'],
'onUpdate' => $value['update_rule'],
];
}
$list[$value['constraint_name']]['local'][] = $value['column_name'];
$list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name'];
}
$result = [];
foreach ($list as $constraint) {
$result[] = new ForeignKeyConstraint(
array_values($constraint['local']),
$constraint['foreignTable'],
array_values($constraint['foreign']),
$constraint['name'],
[
'onDelete' => $constraint['onDelete'],
'onUpdate' => $constraint['onUpdate'],
]
);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function listTableDetails($name)
{
$table = parent::listTableDetails($name);
$platform = $this->_platform;
assert($platform instanceof MySqlPlatform);
$sql = $platform->getListTableMetadataSQL($name);
$tableOptions = $this->_conn->fetchAssoc($sql);
if ($tableOptions === false) {
return $table;
}
$table->addOption('engine', $tableOptions['ENGINE']);
if ($tableOptions['TABLE_COLLATION'] !== null) {
$table->addOption('collation', $tableOptions['TABLE_COLLATION']);
}
if ($tableOptions['AUTO_INCREMENT'] !== null) {
$table->addOption('autoincrement', $tableOptions['AUTO_INCREMENT']);
}
$table->addOption('comment', $tableOptions['TABLE_COMMENT']);
$table->addOption('create_options', $this->parseCreateOptions($tableOptions['CREATE_OPTIONS']));
return $table;
}
/**
* @return string[]|true[]
*/
private function parseCreateOptions(?string $string): array
{
$options = [];
if ($string === null || $string === '') {
return $options;
}
foreach (explode(' ', $string) as $pair) {
$parts = explode('=', $pair, 2);
$options[$parts[0]] = $parts[1] ?? true;
}
return $options;
}
}
lib/Doctrine/DBAL/Schema/OracleSchemaManager.php 0000644 00000030306 13727250467 0015334 0 ustar 00 getPrevious();
assert($exception instanceof Throwable);
if (! $exception instanceof DriverException) {
throw $exception;
}
// If we have a error code 1940 (ORA-01940), the drop database operation failed
// because of active connections on the database.
// To force dropping the database, we first have to close all active connections
// on that database and issue the drop database operation again.
if ($exception->getErrorCode() !== 1940) {
throw $exception;
}
$this->killUserSessions($database);
parent::dropDatabase($database);
}
}
/**
* {@inheritdoc}
*/
protected function _getPortableViewDefinition($view)
{
$view = array_change_key_case($view, CASE_LOWER);
return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']);
}
/**
* {@inheritdoc}
*/
protected function _getPortableUserDefinition($user)
{
$user = array_change_key_case($user, CASE_LOWER);
return [
'user' => $user['username'],
];
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableDefinition($table)
{
$table = array_change_key_case($table, CASE_LOWER);
return $this->getQuotedIdentifierName($table['table_name']);
}
/**
* {@inheritdoc}
*
* @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
$indexBuffer = [];
foreach ($tableIndexes as $tableIndex) {
$tableIndex = array_change_key_case($tableIndex, CASE_LOWER);
$keyName = strtolower($tableIndex['name']);
$buffer = [];
if (strtolower($tableIndex['is_primary']) === 'p') {
$keyName = 'primary';
$buffer['primary'] = true;
$buffer['non_unique'] = false;
} else {
$buffer['primary'] = false;
$buffer['non_unique'] = ! $tableIndex['is_unique'];
}
$buffer['key_name'] = $keyName;
$buffer['column_name'] = $this->getQuotedIdentifierName($tableIndex['column_name']);
$indexBuffer[] = $buffer;
}
return parent::_getPortableTableIndexesList($indexBuffer, $tableName);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableColumnDefinition($tableColumn)
{
$tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
$dbType = strtolower($tableColumn['data_type']);
if (strpos($dbType, 'timestamp(') === 0) {
if (strpos($dbType, 'with time zone')) {
$dbType = 'timestamptz';
} else {
$dbType = 'timestamp';
}
}
$unsigned = $fixed = $precision = $scale = $length = null;
if (! isset($tableColumn['column_name'])) {
$tableColumn['column_name'] = '';
}
// Default values returned from database sometimes have trailing spaces.
$tableColumn['data_default'] = trim($tableColumn['data_default']);
if ($tableColumn['data_default'] === '' || $tableColumn['data_default'] === 'NULL') {
$tableColumn['data_default'] = null;
}
if ($tableColumn['data_default'] !== null) {
// Default values returned from database are represented as literal expressions
if (preg_match('/^\'(.*)\'$/s', $tableColumn['data_default'], $matches)) {
$tableColumn['data_default'] = str_replace("''", "'", $matches[1]);
}
}
if ($tableColumn['data_precision'] !== null) {
$precision = (int) $tableColumn['data_precision'];
}
if ($tableColumn['data_scale'] !== null) {
$scale = (int) $tableColumn['data_scale'];
}
$type = $this->_platform->getDoctrineTypeMapping($dbType);
$type = $this->extractDoctrineTypeFromComment($tableColumn['comments'], $type);
$tableColumn['comments'] = $this->removeDoctrineTypeFromComment($tableColumn['comments'], $type);
switch ($dbType) {
case 'number':
if ($precision === 20 && $scale === 0) {
$type = 'bigint';
} elseif ($precision === 5 && $scale === 0) {
$type = 'smallint';
} elseif ($precision === 1 && $scale === 0) {
$type = 'boolean';
} elseif ($scale > 0) {
$type = 'decimal';
}
break;
case 'varchar':
case 'varchar2':
case 'nvarchar2':
$length = $tableColumn['char_length'];
$fixed = false;
break;
case 'char':
case 'nchar':
$length = $tableColumn['char_length'];
$fixed = true;
break;
}
$options = [
'notnull' => (bool) ($tableColumn['nullable'] === 'N'),
'fixed' => (bool) $fixed,
'unsigned' => (bool) $unsigned,
'default' => $tableColumn['data_default'],
'length' => $length,
'precision' => $precision,
'scale' => $scale,
'comment' => isset($tableColumn['comments']) && $tableColumn['comments'] !== ''
? $tableColumn['comments']
: null,
];
return new Column($this->getQuotedIdentifierName($tableColumn['column_name']), Type::getType($type), $options);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableForeignKeysList($tableForeignKeys)
{
$list = [];
foreach ($tableForeignKeys as $value) {
$value = array_change_key_case($value, CASE_LOWER);
if (! isset($list[$value['constraint_name']])) {
if ($value['delete_rule'] === 'NO ACTION') {
$value['delete_rule'] = null;
}
$list[$value['constraint_name']] = [
'name' => $this->getQuotedIdentifierName($value['constraint_name']),
'local' => [],
'foreign' => [],
'foreignTable' => $value['references_table'],
'onDelete' => $value['delete_rule'],
];
}
$localColumn = $this->getQuotedIdentifierName($value['local_column']);
$foreignColumn = $this->getQuotedIdentifierName($value['foreign_column']);
$list[$value['constraint_name']]['local'][$value['position']] = $localColumn;
$list[$value['constraint_name']]['foreign'][$value['position']] = $foreignColumn;
}
$result = [];
foreach ($list as $constraint) {
$result[] = new ForeignKeyConstraint(
array_values($constraint['local']),
$this->getQuotedIdentifierName($constraint['foreignTable']),
array_values($constraint['foreign']),
$this->getQuotedIdentifierName($constraint['name']),
['onDelete' => $constraint['onDelete']]
);
}
return $result;
}
/**
* {@inheritdoc}
*/
protected function _getPortableSequenceDefinition($sequence)
{
$sequence = array_change_key_case($sequence, CASE_LOWER);
return new Sequence(
$this->getQuotedIdentifierName($sequence['sequence_name']),
(int) $sequence['increment_by'],
(int) $sequence['min_value']
);
}
/**
* {@inheritdoc}
*
* @deprecated
*/
protected function _getPortableFunctionDefinition($function)
{
$function = array_change_key_case($function, CASE_LOWER);
return $function['name'];
}
/**
* {@inheritdoc}
*/
protected function _getPortableDatabaseDefinition($database)
{
$database = array_change_key_case($database, CASE_LOWER);
return $database['username'];
}
/**
* {@inheritdoc}
*
* @param string|null $database
*
* Calling this method without an argument or by passing NULL is deprecated.
*/
public function createDatabase($database = null)
{
if ($database === null) {
$database = $this->_conn->getDatabase();
}
$params = $this->_conn->getParams();
$username = $database;
$password = $params['password'];
$query = 'CREATE USER ' . $username . ' IDENTIFIED BY ' . $password;
$this->_conn->executeUpdate($query);
$query = 'GRANT DBA TO ' . $username;
$this->_conn->executeUpdate($query);
}
/**
* @param string $table
*
* @return bool
*/
public function dropAutoincrement($table)
{
assert($this->_platform instanceof OraclePlatform);
$sql = $this->_platform->getDropAutoincrementSql($table);
foreach ($sql as $query) {
$this->_conn->executeUpdate($query);
}
return true;
}
/**
* {@inheritdoc}
*/
public function dropTable($name)
{
$this->tryMethod('dropAutoincrement', $name);
parent::dropTable($name);
}
/**
* Returns the quoted representation of the given identifier name.
*
* Quotes non-uppercase identifiers explicitly to preserve case
* and thus make references to the particular identifier work.
*
* @param string $identifier The identifier to quote.
*
* @return string The quoted identifier.
*/
private function getQuotedIdentifierName($identifier)
{
if (preg_match('/[a-z]/', $identifier)) {
return $this->_platform->quoteIdentifier($identifier);
}
return $identifier;
}
/**
* Kills sessions connected with the given user.
*
* This is useful to force DROP USER operations which could fail because of active user sessions.
*
* @param string $user The name of the user to kill sessions for.
*
* @return void
*/
private function killUserSessions($user)
{
$sql = <<' . '' . $table->getName() . ' | ||
' . '' . $columnLabel . '' . ' | ' . '' . '' . strtolower($column->getType()) . '' . ' | ' . ''; $primaryKey = $table->getPrimaryKey(); if ($primaryKey !== null && in_array($column->getName(), $primaryKey->getColumns())) { $label .= "\xe2\x9c\xb7"; } $label .= ' |