File "OraclePlatform-20250328220110.php"
Full Path: /home/pulsehostuk9/public_html/invoicer.pulsehost.co.uk/vendor/doctrine/dbal/src/Platforms/OraclePlatform-20250328220110.php
File size: 23.96 KB
MIME-type: text/x-php
Charset: utf-8
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Platforms;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception\InvalidColumnType\ColumnLengthRequired;
use Doctrine\DBAL\Platforms\Keywords\KeywordList;
use Doctrine\DBAL\Platforms\Keywords\OracleKeywords;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Identifier;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\OracleSchemaManager;
use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\TableDiff;
use Doctrine\DBAL\TransactionIsolationLevel;
use Doctrine\DBAL\Types\Types;
use InvalidArgumentException;
use function array_merge;
use function count;
use function explode;
use function implode;
use function sprintf;
use function str_contains;
use function strlen;
use function strtoupper;
use function substr;
/**
* OraclePlatform.
*/
class OraclePlatform extends AbstractPlatform
{
public function getSubstringExpression(string $string, string $start, ?string $length = null): string
{
if ($length === null) {
return sprintf('SUBSTR(%s, %s)', $string, $start);
}
return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length);
}
public function getLocateExpression(string $string, string $substring, ?string $start = null): string
{
if ($start === null) {
return sprintf('INSTR(%s, %s)', $string, $substring);
}
return sprintf('INSTR(%s, %s, %s)', $string, $substring, $start);
}
protected function getDateArithmeticIntervalExpression(
string $date,
string $operator,
string $interval,
DateIntervalUnit $unit,
): string {
switch ($unit) {
case DateIntervalUnit::MONTH:
case DateIntervalUnit::QUARTER:
case DateIntervalUnit::YEAR:
switch ($unit) {
case DateIntervalUnit::QUARTER:
$interval = $this->multiplyInterval($interval, 3);
break;
case DateIntervalUnit::YEAR:
$interval = $this->multiplyInterval($interval, 12);
break;
}
return 'ADD_MONTHS(' . $date . ', ' . $operator . $interval . ')';
default:
$calculationClause = '';
switch ($unit) {
case DateIntervalUnit::SECOND:
$calculationClause = '/24/60/60';
break;
case DateIntervalUnit::MINUTE:
$calculationClause = '/24/60';
break;
case DateIntervalUnit::HOUR:
$calculationClause = '/24';
break;
case DateIntervalUnit::WEEK:
$calculationClause = '*7';
break;
}
return '(' . $date . $operator . $interval . $calculationClause . ')';
}
}
public function getDateDiffExpression(string $date1, string $date2): string
{
return sprintf('TRUNC(%s) - TRUNC(%s)', $date1, $date2);
}
public function getBitAndComparisonExpression(string $value1, string $value2): string
{
return 'BITAND(' . $value1 . ', ' . $value2 . ')';
}
public function getCurrentDatabaseExpression(): string
{
return "SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA')";
}
public function getBitOrComparisonExpression(string $value1, string $value2): string
{
return '(' . $value1 . '-' .
$this->getBitAndComparisonExpression($value1, $value2)
. '+' . $value2 . ')';
}
public function getCreatePrimaryKeySQL(Index $index, string $table): string
{
return 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $index->getQuotedName($this)
. ' PRIMARY KEY (' . implode(', ', $index->getQuotedColumns($this)) . ')';
}
/**
* {@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): string
{
return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
' START WITH ' . $sequence->getInitialValue() .
' MINVALUE ' . $sequence->getInitialValue() .
' INCREMENT BY ' . $sequence->getAllocationSize() .
$this->getSequenceCacheSQL($sequence);
}
public function getAlterSequenceSQL(Sequence $sequence): string
{
return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
' INCREMENT BY ' . $sequence->getAllocationSize()
. $this->getSequenceCacheSQL($sequence);
}
/**
* Cache definition for sequences
*/
private function getSequenceCacheSQL(Sequence $sequence): string
{
if ($sequence->getCache() === 0) {
return ' NOCACHE';
}
if ($sequence->getCache() === 1) {
return ' NOCACHE';
}
if ($sequence->getCache() > 1) {
return ' CACHE ' . $sequence->getCache();
}
return '';
}
public function getSequenceNextValSQL(string $sequence): string
{
return 'SELECT ' . $sequence . '.nextval FROM DUAL';
}
public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string
{
return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
}
protected function _getTransactionIsolationLevelSQL(TransactionIsolationLevel $level): string
{
return match ($level) {
TransactionIsolationLevel::READ_UNCOMMITTED => 'READ UNCOMMITTED',
TransactionIsolationLevel::READ_COMMITTED => 'READ COMMITTED',
TransactionIsolationLevel::REPEATABLE_READ,
TransactionIsolationLevel::SERIALIZABLE => 'SERIALIZABLE',
};
}
/**
* {@inheritDoc}
*/
public function getBooleanTypeDeclarationSQL(array $column): string
{
return 'NUMBER(1)';
}
/**
* {@inheritDoc}
*/
public function getIntegerTypeDeclarationSQL(array $column): string
{
return 'NUMBER(10)';
}
/**
* {@inheritDoc}
*/
public function getBigIntTypeDeclarationSQL(array $column): string
{
return 'NUMBER(20)';
}
/**
* {@inheritDoc}
*/
public function getSmallIntTypeDeclarationSQL(array $column): string
{
return 'NUMBER(5)';
}
/**
* {@inheritDoc}
*/
public function getDateTimeTypeDeclarationSQL(array $column): string
{
return 'TIMESTAMP(0)';
}
/**
* {@inheritDoc}
*/
public function getDateTimeTzTypeDeclarationSQL(array $column): string
{
return 'TIMESTAMP(0) WITH TIME ZONE';
}
/**
* {@inheritDoc}
*/
public function getDateTypeDeclarationSQL(array $column): string
{
return 'DATE';
}
/**
* {@inheritDoc}
*/
public function getTimeTypeDeclarationSQL(array $column): string
{
return 'DATE';
}
/**
* {@inheritDoc}
*/
protected function _getCommonIntegerTypeDeclarationSQL(array $column): string
{
return '';
}
protected function getVarcharTypeDeclarationSQLSnippet(?int $length): string
{
if ($length === null) {
throw ColumnLengthRequired::new($this, 'VARCHAR2');
}
return sprintf('VARCHAR2(%d)', $length);
}
protected function getBinaryTypeDeclarationSQLSnippet(?int $length): string
{
if ($length === null) {
throw ColumnLengthRequired::new($this, 'RAW');
}
return sprintf('RAW(%d)', $length);
}
protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length): string
{
return $this->getBinaryTypeDeclarationSQLSnippet($length);
}
/**
* {@inheritDoc}
*/
public function getClobTypeDeclarationSQL(array $column): string
{
return 'CLOB';
}
/** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */
public function getListDatabasesSQL(): string
{
return 'SELECT username FROM all_users';
}
/** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */
public function getListSequencesSQL(string $database): string
{
return 'SELECT SEQUENCE_NAME, MIN_VALUE, INCREMENT_BY FROM SYS.ALL_SEQUENCES WHERE SEQUENCE_OWNER = '
. $this->quoteStringLiteral(
$this->normalizeIdentifier($database)->getName(),
);
}
/**
* {@inheritDoc}
*/
protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array
{
$indexes = $options['indexes'] ?? [];
$options['indexes'] = [];
$sql = parent::_getCreateTableSQL($name, $columns, $options);
foreach ($columns as $column) {
if (isset($column['sequence'])) {
$sql[] = $this->getCreateSequenceSQL($column['sequence']);
}
if (
empty($column['autoincrement'])
) {
continue;
}
$sql = array_merge($sql, $this->getCreateAutoincrementSql($column['name'], $name));
}
foreach ($indexes as $index) {
$sql[] = $this->getCreateIndexSQL($index, $name);
}
return $sql;
}
/** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */
public function getListViewsSQL(string $database): string
{
return 'SELECT view_name, text FROM sys.user_views';
}
/** @return array<int, string> */
protected function getCreateAutoincrementSql(string $name, string $table, int $start = 1): array
{
$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->getCreateIndexSQL($idx, $quotedTableName) . "';
END IF;
END;";
$sequenceName = $this->getIdentitySequenceName(
$tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName,
);
$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
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;
SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL;
END IF;
END;';
return $sql;
}
/**
* @internal The method should be only used from within the OracleSchemaManager class hierarchy.
*
* 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(string $table): array
{
$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.
*/
private function normalizeIdentifier(string $name): Identifier
{
$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.
*/
private function getAutoincrementIdentifierName(Identifier $table): string
{
$identifierName = $this->addSuffix($table->getName(), '_AI_PK');
return $table->isQuoted()
? $this->quoteSingleIdentifier($identifierName)
: $identifierName;
}
public function getDropForeignKeySQL(string $foreignKey, string $table): string
{
return $this->getDropConstraintSQL($foreignKey, $table);
}
/** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string
{
$referentialAction = '';
if ($foreignKey->hasOption('onDelete')) {
$referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
}
if ($referentialAction !== '') {
return ' ON DELETE ' . $referentialAction;
}
return '';
}
/** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
public function getForeignKeyReferentialActionSQL(string $action): string
{
$action = strtoupper($action);
return match ($action) {
'RESTRICT',
'NO ACTION' => '',
'CASCADE',
'SET NULL' => $action,
default => throw new InvalidArgumentException(sprintf('Invalid foreign key action "%s".', $action)),
};
}
public function getCreateDatabaseSQL(string $name): string
{
return 'CREATE USER ' . $name;
}
public function getDropDatabaseSQL(string $name): string
{
return 'DROP USER ' . $name . ' CASCADE';
}
/**
* {@inheritDoc}
*/
public function getAlterTableSQL(TableDiff $diff): array
{
$sql = [];
$commentsSQL = [];
$columnSql = [];
$addColumnSQL = [];
$tableNameSQL = $diff->getOldTable()->getQuotedName($this);
foreach ($diff->getAddedColumns() as $column) {
$addColumnSQL[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
$comment = $column->getComment();
if ($comment === '') {
continue;
}
$commentsSQL[] = $this->getCommentOnColumnSQL(
$tableNameSQL,
$column->getQuotedName($this),
$comment,
);
}
if (count($addColumnSQL) > 0) {
$sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ADD (' . implode(', ', $addColumnSQL) . ')';
}
$modifyColumnSQL = [];
foreach ($diff->getModifiedColumns() as $columnDiff) {
$newColumn = $columnDiff->getNewColumn();
$oldColumn = $columnDiff->getOldColumn();
$newColumnProperties = $newColumn->toArray();
$oldColumnProperties = $oldColumn->toArray();
$oldSQL = $this->getColumnDeclarationSQL('', $oldColumnProperties);
$newSQL = $this->getColumnDeclarationSQL('', $newColumnProperties);
if ($newSQL !== $oldSQL) {
if (! $columnDiff->hasNotNullChanged()) {
unset($newColumnProperties['notnull']);
$newSQL = $this->getColumnDeclarationSQL('', $newColumnProperties);
}
$modifyColumnSQL[] = $newColumn->getQuotedName($this) . $newSQL;
}
if (! $columnDiff->hasCommentChanged()) {
continue;
}
$commentsSQL[] = $this->getCommentOnColumnSQL(
$tableNameSQL,
$newColumn->getQuotedName($this),
$newColumn->getComment(),
);
}
if (count($modifyColumnSQL) > 0) {
$sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY (' . implode(', ', $modifyColumnSQL) . ')';
}
foreach ($diff->getRenamedColumns() as $oldColumnName => $column) {
$oldColumnName = new Identifier($oldColumnName);
$sql[] = 'ALTER TABLE ' . $tableNameSQL . ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this)
. ' TO ' . $column->getQuotedName($this);
}
$dropColumnSQL = [];
foreach ($diff->getDroppedColumns() as $column) {
$dropColumnSQL[] = $column->getQuotedName($this);
}
if (count($dropColumnSQL) > 0) {
$sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP (' . implode(', ', $dropColumnSQL) . ')';
}
return array_merge(
$this->getPreAlterTableIndexForeignKeySQL($diff),
$sql,
$commentsSQL,
$this->getPostAlterTableIndexForeignKeySQL($diff),
$columnSql,
);
}
/**
* {@inheritDoc}
*
* @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.
*/
public function getColumnDeclarationSQL(string $name, array $column): string
{
if (isset($column['columnDefinition'])) {
$declaration = $column['columnDefinition'];
} else {
$default = $this->getDefaultValueDeclarationSQL($column);
$notnull = '';
if (isset($column['notnull'])) {
$notnull = $column['notnull'] ? ' NOT NULL' : ' NULL';
}
$typeDecl = $column['type']->getSQLDeclaration($column, $this);
$declaration = $typeDecl . $default . $notnull;
}
return $name . ' ' . $declaration;
}
/**
* {@inheritDoc}
*/
protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName): array
{
if (str_contains($tableName, '.')) {
[$schema] = explode('.', $tableName);
$oldIndexName = $schema . '.' . $oldIndexName;
}
return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)];
}
protected function getIdentitySequenceName(string $tableName): string
{
$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);
}
/** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */
public function supportsCommentOnStatement(): bool
{
return true;
}
protected function doModifyLimitQuery(string $query, ?int $limit, int $offset): string
{
if ($offset > 0) {
$query .= sprintf(' OFFSET %d ROWS', $offset);
}
if ($limit !== null) {
$query .= sprintf(' FETCH NEXT %d ROWS ONLY', $limit);
}
return $query;
}
public function getCreateTemporaryTableSnippetSQL(): string
{
return 'CREATE GLOBAL TEMPORARY TABLE';
}
public function getDateTimeTzFormatString(): string
{
return 'Y-m-d H:i:sP';
}
public function getDateFormatString(): string
{
return 'Y-m-d 00:00:00';
}
public function getTimeFormatString(): string
{
return '1900-01-01 H:i:s';
}
public function getMaxIdentifierLength(): int
{
return 128;
}
public function supportsSequences(): bool
{
return true;
}
public function supportsReleaseSavepoints(): bool
{
return false;
}
public function getTruncateTableSQL(string $tableName, bool $cascade = false): string
{
$tableIdentifier = new Identifier($tableName);
return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this);
}
public function getDummySelectSQL(string $expression = '1'): string
{
return sprintf('SELECT %s FROM DUAL', $expression);
}
protected function initializeDoctrineTypeMappings(): void
{
$this->doctrineTypeMapping = [
'binary_double' => Types::FLOAT,
'binary_float' => Types::FLOAT,
'binary_integer' => Types::BOOLEAN,
'blob' => Types::BLOB,
'char' => Types::STRING,
'clob' => Types::TEXT,
'date' => Types::DATE_MUTABLE,
'float' => Types::FLOAT,
'integer' => Types::INTEGER,
'long' => Types::STRING,
'long raw' => Types::BLOB,
'nchar' => Types::STRING,
'nclob' => Types::TEXT,
'number' => Types::INTEGER,
'nvarchar2' => Types::STRING,
'pls_integer' => Types::BOOLEAN,
'raw' => Types::BINARY,
'rowid' => Types::STRING,
'timestamp' => Types::DATETIME_MUTABLE,
'timestamptz' => Types::DATETIMETZ_MUTABLE,
'urowid' => Types::STRING,
'varchar' => Types::STRING,
'varchar2' => Types::STRING,
];
}
public function releaseSavePoint(string $savepoint): string
{
return '';
}
protected function createReservedKeywordsList(): KeywordList
{
return new OracleKeywords();
}
/**
* {@inheritDoc}
*/
public function getBlobTypeDeclarationSQL(array $column): string
{
return 'BLOB';
}
public function createSchemaManager(Connection $connection): OracleSchemaManager
{
return new OracleSchemaManager($connection, $this);
}
}