vendor/doctrine/dbal/src/Platforms/AbstractPlatform.php line 3726

Open in your IDE?
  1. <?php
  2. namespace Doctrine\DBAL\Platforms;
  3. use Doctrine\Common\EventManager;
  4. use Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs;
  5. use Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs;
  6. use Doctrine\DBAL\Event\SchemaAlterTableEventArgs;
  7. use Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs;
  8. use Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs;
  9. use Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs;
  10. use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
  11. use Doctrine\DBAL\Event\SchemaDropTableEventArgs;
  12. use Doctrine\DBAL\Events;
  13. use Doctrine\DBAL\Exception;
  14. use Doctrine\DBAL\Exception\InvalidLockMode;
  15. use Doctrine\DBAL\LockMode;
  16. use Doctrine\DBAL\Platforms\Keywords\KeywordList;
  17. use Doctrine\DBAL\Schema\Column;
  18. use Doctrine\DBAL\Schema\ColumnDiff;
  19. use Doctrine\DBAL\Schema\Constraint;
  20. use Doctrine\DBAL\Schema\ForeignKeyConstraint;
  21. use Doctrine\DBAL\Schema\Identifier;
  22. use Doctrine\DBAL\Schema\Index;
  23. use Doctrine\DBAL\Schema\Sequence;
  24. use Doctrine\DBAL\Schema\Table;
  25. use Doctrine\DBAL\Schema\TableDiff;
  26. use Doctrine\DBAL\Schema\UniqueConstraint;
  27. use Doctrine\DBAL\SQL\Parser;
  28. use Doctrine\DBAL\TransactionIsolationLevel;
  29. use Doctrine\DBAL\Types;
  30. use Doctrine\DBAL\Types\Type;
  31. use Doctrine\Deprecations\Deprecation;
  32. use InvalidArgumentException;
  33. use UnexpectedValueException;
  34. use function addcslashes;
  35. use function array_map;
  36. use function array_merge;
  37. use function array_unique;
  38. use function array_values;
  39. use function assert;
  40. use function count;
  41. use function explode;
  42. use function func_get_arg;
  43. use function func_get_args;
  44. use function func_num_args;
  45. use function implode;
  46. use function in_array;
  47. use function is_array;
  48. use function is_bool;
  49. use function is_int;
  50. use function is_string;
  51. use function preg_quote;
  52. use function preg_replace;
  53. use function sprintf;
  54. use function str_replace;
  55. use function strlen;
  56. use function strpos;
  57. use function strtolower;
  58. use function strtoupper;
  59. /**
  60.  * Base class for all DatabasePlatforms. The DatabasePlatforms are the central
  61.  * point of abstraction of platform-specific behaviors, features and SQL dialects.
  62.  * They are a passive source of information.
  63.  *
  64.  * @todo Remove any unnecessary methods.
  65.  */
  66. abstract class AbstractPlatform
  67. {
  68.     public const CREATE_INDEXES 1;
  69.     public const CREATE_FOREIGNKEYS 2;
  70.     /** @var string[]|null */
  71.     protected $doctrineTypeMapping;
  72.     /**
  73.      * Contains a list of all columns that should generate parseable column comments for type-detection
  74.      * in reverse engineering scenarios.
  75.      *
  76.      * @deprecated This property is deprecated and will be removed in Doctrine DBAL 4.0.
  77.      *
  78.      * @var string[]|null
  79.      */
  80.     protected $doctrineTypeComments;
  81.     /** @var EventManager|null */
  82.     protected $_eventManager;
  83.     /**
  84.      * Holds the KeywordList instance for the current platform.
  85.      *
  86.      * @var KeywordList|null
  87.      */
  88.     protected $_keywords;
  89.     /**
  90.      * Sets the EventManager used by the Platform.
  91.      *
  92.      * @return void
  93.      */
  94.     public function setEventManager(EventManager $eventManager)
  95.     {
  96.         $this->_eventManager $eventManager;
  97.     }
  98.     /**
  99.      * Gets the EventManager used by the Platform.
  100.      *
  101.      * @return EventManager|null
  102.      */
  103.     public function getEventManager()
  104.     {
  105.         return $this->_eventManager;
  106.     }
  107.     /**
  108.      * Returns the SQL snippet that declares a boolean column.
  109.      *
  110.      * @param mixed[] $column
  111.      *
  112.      * @return string
  113.      */
  114.     abstract public function getBooleanTypeDeclarationSQL(array $column);
  115.     /**
  116.      * Returns the SQL snippet that declares a 4 byte integer column.
  117.      *
  118.      * @param mixed[] $column
  119.      *
  120.      * @return string
  121.      */
  122.     abstract public function getIntegerTypeDeclarationSQL(array $column);
  123.     /**
  124.      * Returns the SQL snippet that declares an 8 byte integer column.
  125.      *
  126.      * @param mixed[] $column
  127.      *
  128.      * @return string
  129.      */
  130.     abstract public function getBigIntTypeDeclarationSQL(array $column);
  131.     /**
  132.      * Returns the SQL snippet that declares a 2 byte integer column.
  133.      *
  134.      * @param mixed[] $column
  135.      *
  136.      * @return string
  137.      */
  138.     abstract public function getSmallIntTypeDeclarationSQL(array $column);
  139.     /**
  140.      * Returns the SQL snippet that declares common properties of an integer column.
  141.      *
  142.      * @param mixed[] $column
  143.      *
  144.      * @return string
  145.      */
  146.     abstract protected function _getCommonIntegerTypeDeclarationSQL(array $column);
  147.     /**
  148.      * Lazy load Doctrine Type Mappings.
  149.      *
  150.      * @return void
  151.      */
  152.     abstract protected function initializeDoctrineTypeMappings();
  153.     /**
  154.      * Initializes Doctrine Type Mappings with the platform defaults
  155.      * and with all additional type mappings.
  156.      */
  157.     private function initializeAllDoctrineTypeMappings(): void
  158.     {
  159.         $this->initializeDoctrineTypeMappings();
  160.         foreach (Type::getTypesMap() as $typeName => $className) {
  161.             foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) {
  162.                 $this->doctrineTypeMapping[$dbType] = $typeName;
  163.             }
  164.         }
  165.     }
  166.     /**
  167.      * Returns the SQL snippet used to declare a column that can
  168.      * store characters in the ASCII character set
  169.      *
  170.      * @param mixed[] $column
  171.      */
  172.     public function getAsciiStringTypeDeclarationSQL(array $column): string
  173.     {
  174.         return $this->getVarcharTypeDeclarationSQL($column);
  175.     }
  176.     /**
  177.      * Returns the SQL snippet used to declare a VARCHAR column type.
  178.      *
  179.      * @param mixed[] $column
  180.      *
  181.      * @return string
  182.      */
  183.     public function getVarcharTypeDeclarationSQL(array $column)
  184.     {
  185.         if (! isset($column['length'])) {
  186.             $column['length'] = $this->getVarcharDefaultLength();
  187.         }
  188.         $fixed $column['fixed'] ?? false;
  189.         $maxLength $fixed
  190.             $this->getCharMaxLength()
  191.             : $this->getVarcharMaxLength();
  192.         if ($column['length'] > $maxLength) {
  193.             return $this->getClobTypeDeclarationSQL($column);
  194.         }
  195.         return $this->getVarcharTypeDeclarationSQLSnippet($column['length'], $fixed);
  196.     }
  197.     /**
  198.      * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
  199.      *
  200.      * @param mixed[] $column The column definition.
  201.      *
  202.      * @return string
  203.      */
  204.     public function getBinaryTypeDeclarationSQL(array $column)
  205.     {
  206.         if (! isset($column['length'])) {
  207.             $column['length'] = $this->getBinaryDefaultLength();
  208.         }
  209.         $fixed $column['fixed'] ?? false;
  210.         $maxLength $this->getBinaryMaxLength();
  211.         if ($column['length'] > $maxLength) {
  212.             if ($maxLength 0) {
  213.                 Deprecation::trigger(
  214.                     'doctrine/dbal',
  215.                     'https://github.com/doctrine/dbal/issues/3187',
  216.                     'Binary column length %d is greater than supported by the platform (%d).'
  217.                         ' Reduce the column length or use a BLOB column instead.',
  218.                     $column['length'],
  219.                     $maxLength
  220.                 );
  221.             }
  222.             return $this->getBlobTypeDeclarationSQL($column);
  223.         }
  224.         return $this->getBinaryTypeDeclarationSQLSnippet($column['length'], $fixed);
  225.     }
  226.     /**
  227.      * Returns the SQL snippet to declare a GUID/UUID column.
  228.      *
  229.      * By default this maps directly to a CHAR(36) and only maps to more
  230.      * special datatypes when the underlying databases support this datatype.
  231.      *
  232.      * @param mixed[] $column
  233.      *
  234.      * @return string
  235.      */
  236.     public function getGuidTypeDeclarationSQL(array $column)
  237.     {
  238.         $column['length'] = 36;
  239.         $column['fixed']  = true;
  240.         return $this->getVarcharTypeDeclarationSQL($column);
  241.     }
  242.     /**
  243.      * Returns the SQL snippet to declare a JSON column.
  244.      *
  245.      * By default this maps directly to a CLOB and only maps to more
  246.      * special datatypes when the underlying databases support this datatype.
  247.      *
  248.      * @param mixed[] $column
  249.      *
  250.      * @return string
  251.      */
  252.     public function getJsonTypeDeclarationSQL(array $column)
  253.     {
  254.         return $this->getClobTypeDeclarationSQL($column);
  255.     }
  256.     /**
  257.      * @param int|false $length
  258.      * @param bool      $fixed
  259.      *
  260.      * @return string
  261.      *
  262.      * @throws Exception If not supported on this platform.
  263.      */
  264.     protected function getVarcharTypeDeclarationSQLSnippet($length$fixed)
  265.     {
  266.         throw Exception::notSupported('VARCHARs not supported by Platform.');
  267.     }
  268.     /**
  269.      * Returns the SQL snippet used to declare a BINARY/VARBINARY column type.
  270.      *
  271.      * @param int|false $length The length of the column.
  272.      * @param bool      $fixed  Whether the column length is fixed.
  273.      *
  274.      * @return string
  275.      *
  276.      * @throws Exception If not supported on this platform.
  277.      */
  278.     protected function getBinaryTypeDeclarationSQLSnippet($length$fixed)
  279.     {
  280.         throw Exception::notSupported('BINARY/VARBINARY column types are not supported by this platform.');
  281.     }
  282.     /**
  283.      * Returns the SQL snippet used to declare a CLOB column type.
  284.      *
  285.      * @param mixed[] $column
  286.      *
  287.      * @return string
  288.      */
  289.     abstract public function getClobTypeDeclarationSQL(array $column);
  290.     /**
  291.      * Returns the SQL Snippet used to declare a BLOB column type.
  292.      *
  293.      * @param mixed[] $column
  294.      *
  295.      * @return string
  296.      */
  297.     abstract public function getBlobTypeDeclarationSQL(array $column);
  298.     /**
  299.      * Gets the name of the platform.
  300.      *
  301.      * @deprecated Identify platforms by their class.
  302.      *
  303.      * @return string
  304.      */
  305.     abstract public function getName();
  306.     /**
  307.      * Registers a doctrine type to be used in conjunction with a column type of this platform.
  308.      *
  309.      * @param string $dbType
  310.      * @param string $doctrineType
  311.      *
  312.      * @return void
  313.      *
  314.      * @throws Exception If the type is not found.
  315.      */
  316.     public function registerDoctrineTypeMapping($dbType$doctrineType)
  317.     {
  318.         if ($this->doctrineTypeMapping === null) {
  319.             $this->initializeAllDoctrineTypeMappings();
  320.         }
  321.         if (! Types\Type::hasType($doctrineType)) {
  322.             throw Exception::typeNotFound($doctrineType);
  323.         }
  324.         $dbType                             strtolower($dbType);
  325.         $this->doctrineTypeMapping[$dbType] = $doctrineType;
  326.         $doctrineType Type::getType($doctrineType);
  327.         if (! $doctrineType->requiresSQLCommentHint($this)) {
  328.             return;
  329.         }
  330.         $this->markDoctrineTypeCommented($doctrineType);
  331.     }
  332.     /**
  333.      * Gets the Doctrine type that is mapped for the given database column type.
  334.      *
  335.      * @param string $dbType
  336.      *
  337.      * @return string
  338.      *
  339.      * @throws Exception
  340.      */
  341.     public function getDoctrineTypeMapping($dbType)
  342.     {
  343.         if ($this->doctrineTypeMapping === null) {
  344.             $this->initializeAllDoctrineTypeMappings();
  345.         }
  346.         $dbType strtolower($dbType);
  347.         if (! isset($this->doctrineTypeMapping[$dbType])) {
  348.             throw new Exception(
  349.                 'Unknown database type ' $dbType ' requested, ' . static::class . ' may not support it.'
  350.             );
  351.         }
  352.         return $this->doctrineTypeMapping[$dbType];
  353.     }
  354.     /**
  355.      * Checks if a database type is currently supported by this platform.
  356.      *
  357.      * @param string $dbType
  358.      *
  359.      * @return bool
  360.      */
  361.     public function hasDoctrineTypeMappingFor($dbType)
  362.     {
  363.         if ($this->doctrineTypeMapping === null) {
  364.             $this->initializeAllDoctrineTypeMappings();
  365.         }
  366.         $dbType strtolower($dbType);
  367.         return isset($this->doctrineTypeMapping[$dbType]);
  368.     }
  369.     /**
  370.      * Initializes the Doctrine Type comments instance variable for in_array() checks.
  371.      *
  372.      * @deprecated This API will be removed in Doctrine DBAL 4.0.
  373.      *
  374.      * @return void
  375.      */
  376.     protected function initializeCommentedDoctrineTypes()
  377.     {
  378.         Deprecation::triggerIfCalledFromOutside(
  379.             'doctrine/dbal',
  380.             'https://github.com/doctrine/dbal/pull/5058',
  381.             '%s is deprecated and will be removed in Doctrine DBAL 4.0.',
  382.             __METHOD__
  383.         );
  384.         $this->doctrineTypeComments = [];
  385.         foreach (Type::getTypesMap() as $typeName => $className) {
  386.             $type Type::getType($typeName);
  387.             if (! $type->requiresSQLCommentHint($this)) {
  388.                 continue;
  389.             }
  390.             $this->doctrineTypeComments[] = $typeName;
  391.         }
  392.     }
  393.     /**
  394.      * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type?
  395.      *
  396.      * @deprecated Use {@link Type::requiresSQLCommentHint()} instead.
  397.      *
  398.      * @return bool
  399.      */
  400.     public function isCommentedDoctrineType(Type $doctrineType)
  401.     {
  402.         Deprecation::triggerIfCalledFromOutside(
  403.             'doctrine/dbal',
  404.             'https://github.com/doctrine/dbal/pull/5058',
  405.             '%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.',
  406.             __METHOD__
  407.         );
  408.         if ($this->doctrineTypeComments === null) {
  409.             $this->initializeCommentedDoctrineTypes();
  410.         }
  411.         return $doctrineType->requiresSQLCommentHint($this);
  412.     }
  413.     /**
  414.      * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements.
  415.      *
  416.      * @param string|Type $doctrineType
  417.      *
  418.      * @return void
  419.      */
  420.     public function markDoctrineTypeCommented($doctrineType)
  421.     {
  422.         Deprecation::triggerIfCalledFromOutside(
  423.             'doctrine/dbal',
  424.             'https://github.com/doctrine/dbal/pull/5058',
  425.             '%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.',
  426.             __METHOD__
  427.         );
  428.         if ($this->doctrineTypeComments === null) {
  429.             $this->initializeCommentedDoctrineTypes();
  430.         }
  431.         assert(is_array($this->doctrineTypeComments));
  432.         $this->doctrineTypeComments[] = $doctrineType instanceof Type $doctrineType->getName() : $doctrineType;
  433.     }
  434.     /**
  435.      * Gets the comment to append to a column comment that helps parsing this type in reverse engineering.
  436.      *
  437.      * @return string
  438.      */
  439.     public function getDoctrineTypeComment(Type $doctrineType)
  440.     {
  441.         return '(DC2Type:' $doctrineType->getName() . ')';
  442.     }
  443.     /**
  444.      * Gets the comment of a passed column modified by potential doctrine type comment hints.
  445.      *
  446.      * @return string|null
  447.      */
  448.     protected function getColumnComment(Column $column)
  449.     {
  450.         $comment $column->getComment();
  451.         if ($column->getType()->requiresSQLCommentHint($this)) {
  452.             $comment .= $this->getDoctrineTypeComment($column->getType());
  453.         }
  454.         return $comment;
  455.     }
  456.     /**
  457.      * Gets the character used for identifier quoting.
  458.      *
  459.      * @return string
  460.      */
  461.     public function getIdentifierQuoteCharacter()
  462.     {
  463.         return '"';
  464.     }
  465.     /**
  466.      * Gets the string portion that starts an SQL comment.
  467.      *
  468.      * @deprecated
  469.      *
  470.      * @return string
  471.      */
  472.     public function getSqlCommentStartString()
  473.     {
  474.         Deprecation::trigger(
  475.             'doctrine/dbal',
  476.             'https://github.com/doctrine/dbal/pulls/4724',
  477.             'AbstractPlatform::getSqlCommentStartString() is deprecated.'
  478.         );
  479.         return '--';
  480.     }
  481.     /**
  482.      * Gets the string portion that ends an SQL comment.
  483.      *
  484.      * @deprecated
  485.      *
  486.      * @return string
  487.      */
  488.     public function getSqlCommentEndString()
  489.     {
  490.         Deprecation::trigger(
  491.             'doctrine/dbal',
  492.             'https://github.com/doctrine/dbal/pulls/4724',
  493.             'AbstractPlatform::getSqlCommentEndString() is deprecated.'
  494.         );
  495.         return "\n";
  496.     }
  497.     /**
  498.      * Gets the maximum length of a char column.
  499.      *
  500.      * @deprecated
  501.      */
  502.     public function getCharMaxLength(): int
  503.     {
  504.         Deprecation::triggerIfCalledFromOutside(
  505.             'doctrine/dbal',
  506.             'https://github.com/doctrine/dbal/issues/3263',
  507.             'AbstractPlatform::getCharMaxLength() is deprecated.'
  508.         );
  509.         return $this->getVarcharMaxLength();
  510.     }
  511.     /**
  512.      * Gets the maximum length of a varchar column.
  513.      *
  514.      * @deprecated
  515.      *
  516.      * @return int
  517.      */
  518.     public function getVarcharMaxLength()
  519.     {
  520.         Deprecation::triggerIfCalledFromOutside(
  521.             'doctrine/dbal',
  522.             'https://github.com/doctrine/dbal/issues/3263',
  523.             'AbstractPlatform::getVarcharMaxLength() is deprecated.'
  524.         );
  525.         return 4000;
  526.     }
  527.     /**
  528.      * Gets the default length of a varchar column.
  529.      *
  530.      * @deprecated
  531.      *
  532.      * @return int
  533.      */
  534.     public function getVarcharDefaultLength()
  535.     {
  536.         Deprecation::triggerIfCalledFromOutside(
  537.             'doctrine/dbal',
  538.             'https://github.com/doctrine/dbal/issues/3263',
  539.             'Relying on the default varchar column length is deprecated, specify the length explicitly.'
  540.         );
  541.         return 255;
  542.     }
  543.     /**
  544.      * Gets the maximum length of a binary column.
  545.      *
  546.      * @deprecated
  547.      *
  548.      * @return int
  549.      */
  550.     public function getBinaryMaxLength()
  551.     {
  552.         Deprecation::triggerIfCalledFromOutside(
  553.             'doctrine/dbal',
  554.             'https://github.com/doctrine/dbal/issues/3263',
  555.             'AbstractPlatform::getBinaryMaxLength() is deprecated.'
  556.         );
  557.         return 4000;
  558.     }
  559.     /**
  560.      * Gets the default length of a binary column.
  561.      *
  562.      * @deprecated
  563.      *
  564.      * @return int
  565.      */
  566.     public function getBinaryDefaultLength()
  567.     {
  568.         Deprecation::trigger(
  569.             'doctrine/dbal',
  570.             'https://github.com/doctrine/dbal/issues/3263',
  571.             'Relying on the default binary column length is deprecated, specify the length explicitly.'
  572.         );
  573.         return 255;
  574.     }
  575.     /**
  576.      * Gets all SQL wildcard characters of the platform.
  577.      *
  578.      * @deprecated Use {@see AbstractPlatform::getLikeWildcardCharacters()} instead.
  579.      *
  580.      * @return string[]
  581.      */
  582.     public function getWildcards()
  583.     {
  584.         Deprecation::trigger(
  585.             'doctrine/dbal',
  586.             'https://github.com/doctrine/dbal/pulls/4724',
  587.             'AbstractPlatform::getWildcards() is deprecated.'
  588.             ' Use AbstractPlatform::getLikeWildcardCharacters() instead.'
  589.         );
  590.         return ['%''_'];
  591.     }
  592.     /**
  593.      * Returns the regular expression operator.
  594.      *
  595.      * @return string
  596.      *
  597.      * @throws Exception If not supported on this platform.
  598.      */
  599.     public function getRegexpExpression()
  600.     {
  601.         throw Exception::notSupported(__METHOD__);
  602.     }
  603.     /**
  604.      * Returns the SQL snippet to get the average value of a column.
  605.      *
  606.      * @deprecated Use AVG() in SQL instead.
  607.      *
  608.      * @param string $column The column to use.
  609.      *
  610.      * @return string Generated SQL including an AVG aggregate function.
  611.      */
  612.     public function getAvgExpression($column)
  613.     {
  614.         Deprecation::trigger(
  615.             'doctrine/dbal',
  616.             'https://github.com/doctrine/dbal/pulls/4724',
  617.             'AbstractPlatform::getAvgExpression() is deprecated. Use AVG() in SQL instead.'
  618.         );
  619.         return 'AVG(' $column ')';
  620.     }
  621.     /**
  622.      * Returns the SQL snippet to get the number of rows (without a NULL value) of a column.
  623.      *
  624.      * If a '*' is used instead of a column the number of selected rows is returned.
  625.      *
  626.      * @deprecated Use COUNT() in SQL instead.
  627.      *
  628.      * @param string|int $column The column to use.
  629.      *
  630.      * @return string Generated SQL including a COUNT aggregate function.
  631.      */
  632.     public function getCountExpression($column)
  633.     {
  634.         Deprecation::trigger(
  635.             'doctrine/dbal',
  636.             'https://github.com/doctrine/dbal/pulls/4724',
  637.             'AbstractPlatform::getCountExpression() is deprecated. Use COUNT() in SQL instead.'
  638.         );
  639.         return 'COUNT(' $column ')';
  640.     }
  641.     /**
  642.      * Returns the SQL snippet to get the highest value of a column.
  643.      *
  644.      * @deprecated Use MAX() in SQL instead.
  645.      *
  646.      * @param string $column The column to use.
  647.      *
  648.      * @return string Generated SQL including a MAX aggregate function.
  649.      */
  650.     public function getMaxExpression($column)
  651.     {
  652.         Deprecation::trigger(
  653.             'doctrine/dbal',
  654.             'https://github.com/doctrine/dbal/pulls/4724',
  655.             'AbstractPlatform::getMaxExpression() is deprecated. Use MAX() in SQL instead.'
  656.         );
  657.         return 'MAX(' $column ')';
  658.     }
  659.     /**
  660.      * Returns the SQL snippet to get the lowest value of a column.
  661.      *
  662.      * @deprecated Use MIN() in SQL instead.
  663.      *
  664.      * @param string $column The column to use.
  665.      *
  666.      * @return string Generated SQL including a MIN aggregate function.
  667.      */
  668.     public function getMinExpression($column)
  669.     {
  670.         Deprecation::trigger(
  671.             'doctrine/dbal',
  672.             'https://github.com/doctrine/dbal/pulls/4724',
  673.             'AbstractPlatform::getMinExpression() is deprecated. Use MIN() in SQL instead.'
  674.         );
  675.         return 'MIN(' $column ')';
  676.     }
  677.     /**
  678.      * Returns the SQL snippet to get the total sum of a column.
  679.      *
  680.      * @deprecated Use SUM() in SQL instead.
  681.      *
  682.      * @param string $column The column to use.
  683.      *
  684.      * @return string Generated SQL including a SUM aggregate function.
  685.      */
  686.     public function getSumExpression($column)
  687.     {
  688.         Deprecation::trigger(
  689.             'doctrine/dbal',
  690.             'https://github.com/doctrine/dbal/pulls/4724',
  691.             'AbstractPlatform::getSumExpression() is deprecated. Use SUM() in SQL instead.'
  692.         );
  693.         return 'SUM(' $column ')';
  694.     }
  695.     // scalar functions
  696.     /**
  697.      * Returns the SQL snippet to get the md5 sum of a column.
  698.      *
  699.      * Note: Not SQL92, but common functionality.
  700.      *
  701.      * @deprecated
  702.      *
  703.      * @param string $column
  704.      *
  705.      * @return string
  706.      */
  707.     public function getMd5Expression($column)
  708.     {
  709.         Deprecation::trigger(
  710.             'doctrine/dbal',
  711.             'https://github.com/doctrine/dbal/pulls/4724',
  712.             'AbstractPlatform::getMd5Expression() is deprecated.'
  713.         );
  714.         return 'MD5(' $column ')';
  715.     }
  716.     /**
  717.      * Returns the SQL snippet to get the length of a text column in characters.
  718.      *
  719.      * @param string $column
  720.      *
  721.      * @return string
  722.      */
  723.     public function getLengthExpression($column)
  724.     {
  725.         return 'LENGTH(' $column ')';
  726.     }
  727.     /**
  728.      * Returns the SQL snippet to get the squared value of a column.
  729.      *
  730.      * @deprecated Use SQRT() in SQL instead.
  731.      *
  732.      * @param string $column The column to use.
  733.      *
  734.      * @return string Generated SQL including an SQRT aggregate function.
  735.      */
  736.     public function getSqrtExpression($column)
  737.     {
  738.         Deprecation::trigger(
  739.             'doctrine/dbal',
  740.             'https://github.com/doctrine/dbal/pulls/4724',
  741.             'AbstractPlatform::getSqrtExpression() is deprecated. Use SQRT() in SQL instead.'
  742.         );
  743.         return 'SQRT(' $column ')';
  744.     }
  745.     /**
  746.      * Returns the SQL snippet to round a numeric column to the number of decimals specified.
  747.      *
  748.      * @deprecated Use ROUND() in SQL instead.
  749.      *
  750.      * @param string     $column
  751.      * @param string|int $decimals
  752.      *
  753.      * @return string
  754.      */
  755.     public function getRoundExpression($column$decimals 0)
  756.     {
  757.         Deprecation::trigger(
  758.             'doctrine/dbal',
  759.             'https://github.com/doctrine/dbal/pulls/4724',
  760.             'AbstractPlatform::getRoundExpression() is deprecated. Use ROUND() in SQL instead.'
  761.         );
  762.         return 'ROUND(' $column ', ' $decimals ')';
  763.     }
  764.     /**
  765.      * Returns the SQL snippet to get the remainder of the division operation $expression1 / $expression2.
  766.      *
  767.      * @param string $expression1
  768.      * @param string $expression2
  769.      *
  770.      * @return string
  771.      */
  772.     public function getModExpression($expression1$expression2)
  773.     {
  774.         return 'MOD(' $expression1 ', ' $expression2 ')';
  775.     }
  776.     /**
  777.      * Returns the SQL snippet to trim a string.
  778.      *
  779.      * @param string      $str  The expression to apply the trim to.
  780.      * @param int         $mode The position of the trim (leading/trailing/both).
  781.      * @param string|bool $char The char to trim, has to be quoted already. Defaults to space.
  782.      *
  783.      * @return string
  784.      */
  785.     public function getTrimExpression($str$mode TrimMode::UNSPECIFIED$char false)
  786.     {
  787.         $expression '';
  788.         switch ($mode) {
  789.             case TrimMode::LEADING:
  790.                 $expression 'LEADING ';
  791.                 break;
  792.             case TrimMode::TRAILING:
  793.                 $expression 'TRAILING ';
  794.                 break;
  795.             case TrimMode::BOTH:
  796.                 $expression 'BOTH ';
  797.                 break;
  798.         }
  799.         if ($char !== false) {
  800.             $expression .= $char ' ';
  801.         }
  802.         if ($mode !== TrimMode::UNSPECIFIED || $char !== false) {
  803.             $expression .= 'FROM ';
  804.         }
  805.         return 'TRIM(' $expression $str ')';
  806.     }
  807.     /**
  808.      * Returns the SQL snippet to trim trailing space characters from the expression.
  809.      *
  810.      * @deprecated Use RTRIM() in SQL instead.
  811.      *
  812.      * @param string $str Literal string or column name.
  813.      *
  814.      * @return string
  815.      */
  816.     public function getRtrimExpression($str)
  817.     {
  818.         Deprecation::trigger(
  819.             'doctrine/dbal',
  820.             'https://github.com/doctrine/dbal/pulls/4724',
  821.             'AbstractPlatform::getRtrimExpression() is deprecated. Use RTRIM() in SQL instead.'
  822.         );
  823.         return 'RTRIM(' $str ')';
  824.     }
  825.     /**
  826.      * Returns the SQL snippet to trim leading space characters from the expression.
  827.      *
  828.      * @deprecated Use LTRIM() in SQL instead.
  829.      *
  830.      * @param string $str Literal string or column name.
  831.      *
  832.      * @return string
  833.      */
  834.     public function getLtrimExpression($str)
  835.     {
  836.         Deprecation::trigger(
  837.             'doctrine/dbal',
  838.             'https://github.com/doctrine/dbal/pulls/4724',
  839.             'AbstractPlatform::getLtrimExpression() is deprecated. Use LTRIM() in SQL instead.'
  840.         );
  841.         return 'LTRIM(' $str ')';
  842.     }
  843.     /**
  844.      * Returns the SQL snippet to change all characters from the expression to uppercase,
  845.      * according to the current character set mapping.
  846.      *
  847.      * @deprecated Use UPPER() in SQL instead.
  848.      *
  849.      * @param string $str Literal string or column name.
  850.      *
  851.      * @return string
  852.      */
  853.     public function getUpperExpression($str)
  854.     {
  855.         Deprecation::trigger(
  856.             'doctrine/dbal',
  857.             'https://github.com/doctrine/dbal/pulls/4724',
  858.             'AbstractPlatform::getUpperExpression() is deprecated. Use UPPER() in SQL instead.'
  859.         );
  860.         return 'UPPER(' $str ')';
  861.     }
  862.     /**
  863.      * Returns the SQL snippet to change all characters from the expression to lowercase,
  864.      * according to the current character set mapping.
  865.      *
  866.      * @deprecated Use LOWER() in SQL instead.
  867.      *
  868.      * @param string $str Literal string or column name.
  869.      *
  870.      * @return string
  871.      */
  872.     public function getLowerExpression($str)
  873.     {
  874.         Deprecation::trigger(
  875.             'doctrine/dbal',
  876.             'https://github.com/doctrine/dbal/pulls/4724',
  877.             'AbstractPlatform::getLowerExpression() is deprecated. Use LOWER() in SQL instead.'
  878.         );
  879.         return 'LOWER(' $str ')';
  880.     }
  881.     /**
  882.      * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
  883.      *
  884.      * @param string           $str      Literal string.
  885.      * @param string           $substr   Literal string to find.
  886.      * @param string|int|false $startPos Position to start at, beginning of string by default.
  887.      *
  888.      * @return string
  889.      *
  890.      * @throws Exception If not supported on this platform.
  891.      */
  892.     public function getLocateExpression($str$substr$startPos false)
  893.     {
  894.         throw Exception::notSupported(__METHOD__);
  895.     }
  896.     /**
  897.      * Returns the SQL snippet to get the current system date.
  898.      *
  899.      * @deprecated Generate dates within the application.
  900.      *
  901.      * @return string
  902.      */
  903.     public function getNowExpression()
  904.     {
  905.         Deprecation::trigger(
  906.             'doctrine/dbal',
  907.             'https://github.com/doctrine/dbal/pull/4753',
  908.             'AbstractPlatform::getNowExpression() is deprecated. Generate dates within the application.'
  909.         );
  910.         return 'NOW()';
  911.     }
  912.     /**
  913.      * Returns a SQL snippet to get a substring inside an SQL statement.
  914.      *
  915.      * Note: Not SQL92, but common functionality.
  916.      *
  917.      * SQLite only supports the 2 parameter variant of this function.
  918.      *
  919.      * @param string          $string An sql string literal or column name/alias.
  920.      * @param string|int      $start  Where to start the substring portion.
  921.      * @param string|int|null $length The substring portion length.
  922.      *
  923.      * @return string
  924.      */
  925.     public function getSubstringExpression($string$start$length null)
  926.     {
  927.         if ($length === null) {
  928.             return 'SUBSTRING(' $string ' FROM ' $start ')';
  929.         }
  930.         return 'SUBSTRING(' $string ' FROM ' $start ' FOR ' $length ')';
  931.     }
  932.     /**
  933.      * Returns a SQL snippet to concatenate the given expressions.
  934.      *
  935.      * Accepts an arbitrary number of string parameters. Each parameter must contain an expression.
  936.      *
  937.      * @return string
  938.      */
  939.     public function getConcatExpression()
  940.     {
  941.         return implode(' || 'func_get_args());
  942.     }
  943.     /**
  944.      * Returns the SQL for a logical not.
  945.      *
  946.      * Example:
  947.      * <code>
  948.      * $q = new Doctrine_Query();
  949.      * $e = $q->expr;
  950.      * $q->select('*')->from('table')
  951.      *   ->where($e->eq('id', $e->not('null'));
  952.      * </code>
  953.      *
  954.      * @deprecated Use NOT() in SQL instead.
  955.      *
  956.      * @param string $expression
  957.      *
  958.      * @return string The logical expression.
  959.      */
  960.     public function getNotExpression($expression)
  961.     {
  962.         Deprecation::trigger(
  963.             'doctrine/dbal',
  964.             'https://github.com/doctrine/dbal/pulls/4724',
  965.             'AbstractPlatform::getNotExpression() is deprecated. Use NOT() in SQL instead.'
  966.         );
  967.         return 'NOT(' $expression ')';
  968.     }
  969.     /**
  970.      * Returns the SQL that checks if an expression is null.
  971.      *
  972.      * @deprecated Use IS NULL in SQL instead.
  973.      *
  974.      * @param string $expression The expression that should be compared to null.
  975.      *
  976.      * @return string The logical expression.
  977.      */
  978.     public function getIsNullExpression($expression)
  979.     {
  980.         Deprecation::trigger(
  981.             'doctrine/dbal',
  982.             'https://github.com/doctrine/dbal/pulls/4724',
  983.             'AbstractPlatform::getIsNullExpression() is deprecated. Use IS NULL in SQL instead.'
  984.         );
  985.         return $expression ' IS NULL';
  986.     }
  987.     /**
  988.      * Returns the SQL that checks if an expression is not null.
  989.      *
  990.      * @deprecated Use IS NOT NULL in SQL instead.
  991.      *
  992.      * @param string $expression The expression that should be compared to null.
  993.      *
  994.      * @return string The logical expression.
  995.      */
  996.     public function getIsNotNullExpression($expression)
  997.     {
  998.         Deprecation::trigger(
  999.             'doctrine/dbal',
  1000.             'https://github.com/doctrine/dbal/pulls/4724',
  1001.             'AbstractPlatform::getIsNotNullExpression() is deprecated. Use IS NOT NULL in SQL instead.'
  1002.         );
  1003.         return $expression ' IS NOT NULL';
  1004.     }
  1005.     /**
  1006.      * Returns the SQL that checks if an expression evaluates to a value between two values.
  1007.      *
  1008.      * The parameter $expression is checked if it is between $value1 and $value2.
  1009.      *
  1010.      * Note: There is a slight difference in the way BETWEEN works on some databases.
  1011.      * http://www.w3schools.com/sql/sql_between.asp. If you want complete database
  1012.      * independence you should avoid using between().
  1013.      *
  1014.      * @deprecated Use BETWEEN in SQL instead.
  1015.      *
  1016.      * @param string $expression The value to compare to.
  1017.      * @param string $value1     The lower value to compare with.
  1018.      * @param string $value2     The higher value to compare with.
  1019.      *
  1020.      * @return string The logical expression.
  1021.      */
  1022.     public function getBetweenExpression($expression$value1$value2)
  1023.     {
  1024.         Deprecation::trigger(
  1025.             'doctrine/dbal',
  1026.             'https://github.com/doctrine/dbal/pulls/4724',
  1027.             'AbstractPlatform::getBetweenExpression() is deprecated. Use BETWEEN in SQL instead.'
  1028.         );
  1029.         return $expression ' BETWEEN ' $value1 ' AND ' $value2;
  1030.     }
  1031.     /**
  1032.      * Returns the SQL to get the arccosine of a value.
  1033.      *
  1034.      * @deprecated Use ACOS() in SQL instead.
  1035.      *
  1036.      * @param string $value
  1037.      *
  1038.      * @return string
  1039.      */
  1040.     public function getAcosExpression($value)
  1041.     {
  1042.         Deprecation::trigger(
  1043.             'doctrine/dbal',
  1044.             'https://github.com/doctrine/dbal/pulls/4724',
  1045.             'AbstractPlatform::getAcosExpression() is deprecated. Use ACOS() in SQL instead.'
  1046.         );
  1047.         return 'ACOS(' $value ')';
  1048.     }
  1049.     /**
  1050.      * Returns the SQL to get the sine of a value.
  1051.      *
  1052.      * @deprecated Use SIN() in SQL instead.
  1053.      *
  1054.      * @param string $value
  1055.      *
  1056.      * @return string
  1057.      */
  1058.     public function getSinExpression($value)
  1059.     {
  1060.         Deprecation::trigger(
  1061.             'doctrine/dbal',
  1062.             'https://github.com/doctrine/dbal/pulls/4724',
  1063.             'AbstractPlatform::getSinExpression() is deprecated. Use SIN() in SQL instead.'
  1064.         );
  1065.         return 'SIN(' $value ')';
  1066.     }
  1067.     /**
  1068.      * Returns the SQL to get the PI value.
  1069.      *
  1070.      * @deprecated Use PI() in SQL instead.
  1071.      *
  1072.      * @return string
  1073.      */
  1074.     public function getPiExpression()
  1075.     {
  1076.         Deprecation::trigger(
  1077.             'doctrine/dbal',
  1078.             'https://github.com/doctrine/dbal/pulls/4724',
  1079.             'AbstractPlatform::getPiExpression() is deprecated. Use PI() in SQL instead.'
  1080.         );
  1081.         return 'PI()';
  1082.     }
  1083.     /**
  1084.      * Returns the SQL to get the cosine of a value.
  1085.      *
  1086.      * @deprecated Use COS() in SQL instead.
  1087.      *
  1088.      * @param string $value
  1089.      *
  1090.      * @return string
  1091.      */
  1092.     public function getCosExpression($value)
  1093.     {
  1094.         Deprecation::trigger(
  1095.             'doctrine/dbal',
  1096.             'https://github.com/doctrine/dbal/pulls/4724',
  1097.             'AbstractPlatform::getCosExpression() is deprecated. Use COS() in SQL instead.'
  1098.         );
  1099.         return 'COS(' $value ')';
  1100.     }
  1101.     /**
  1102.      * Returns the SQL to calculate the difference in days between the two passed dates.
  1103.      *
  1104.      * Computes diff = date1 - date2.
  1105.      *
  1106.      * @param string $date1
  1107.      * @param string $date2
  1108.      *
  1109.      * @return string
  1110.      *
  1111.      * @throws Exception If not supported on this platform.
  1112.      */
  1113.     public function getDateDiffExpression($date1$date2)
  1114.     {
  1115.         throw Exception::notSupported(__METHOD__);
  1116.     }
  1117.     /**
  1118.      * Returns the SQL to add the number of given seconds to a date.
  1119.      *
  1120.      * @param string $date
  1121.      * @param int    $seconds
  1122.      *
  1123.      * @return string
  1124.      *
  1125.      * @throws Exception If not supported on this platform.
  1126.      */
  1127.     public function getDateAddSecondsExpression($date$seconds)
  1128.     {
  1129.         return $this->getDateArithmeticIntervalExpression($date'+'$secondsDateIntervalUnit::SECOND);
  1130.     }
  1131.     /**
  1132.      * Returns the SQL to subtract the number of given seconds from a date.
  1133.      *
  1134.      * @param string $date
  1135.      * @param int    $seconds
  1136.      *
  1137.      * @return string
  1138.      *
  1139.      * @throws Exception If not supported on this platform.
  1140.      */
  1141.     public function getDateSubSecondsExpression($date$seconds)
  1142.     {
  1143.         return $this->getDateArithmeticIntervalExpression($date'-'$secondsDateIntervalUnit::SECOND);
  1144.     }
  1145.     /**
  1146.      * Returns the SQL to add the number of given minutes to a date.
  1147.      *
  1148.      * @param string $date
  1149.      * @param int    $minutes
  1150.      *
  1151.      * @return string
  1152.      *
  1153.      * @throws Exception If not supported on this platform.
  1154.      */
  1155.     public function getDateAddMinutesExpression($date$minutes)
  1156.     {
  1157.         return $this->getDateArithmeticIntervalExpression($date'+'$minutesDateIntervalUnit::MINUTE);
  1158.     }
  1159.     /**
  1160.      * Returns the SQL to subtract the number of given minutes from a date.
  1161.      *
  1162.      * @param string $date
  1163.      * @param int    $minutes
  1164.      *
  1165.      * @return string
  1166.      *
  1167.      * @throws Exception If not supported on this platform.
  1168.      */
  1169.     public function getDateSubMinutesExpression($date$minutes)
  1170.     {
  1171.         return $this->getDateArithmeticIntervalExpression($date'-'$minutesDateIntervalUnit::MINUTE);
  1172.     }
  1173.     /**
  1174.      * Returns the SQL to add the number of given hours to a date.
  1175.      *
  1176.      * @param string $date
  1177.      * @param int    $hours
  1178.      *
  1179.      * @return string
  1180.      *
  1181.      * @throws Exception If not supported on this platform.
  1182.      */
  1183.     public function getDateAddHourExpression($date$hours)
  1184.     {
  1185.         return $this->getDateArithmeticIntervalExpression($date'+'$hoursDateIntervalUnit::HOUR);
  1186.     }
  1187.     /**
  1188.      * Returns the SQL to subtract the number of given hours to a date.
  1189.      *
  1190.      * @param string $date
  1191.      * @param int    $hours
  1192.      *
  1193.      * @return string
  1194.      *
  1195.      * @throws Exception If not supported on this platform.
  1196.      */
  1197.     public function getDateSubHourExpression($date$hours)
  1198.     {
  1199.         return $this->getDateArithmeticIntervalExpression($date'-'$hoursDateIntervalUnit::HOUR);
  1200.     }
  1201.     /**
  1202.      * Returns the SQL to add the number of given days to a date.
  1203.      *
  1204.      * @param string $date
  1205.      * @param int    $days
  1206.      *
  1207.      * @return string
  1208.      *
  1209.      * @throws Exception If not supported on this platform.
  1210.      */
  1211.     public function getDateAddDaysExpression($date$days)
  1212.     {
  1213.         return $this->getDateArithmeticIntervalExpression($date'+'$daysDateIntervalUnit::DAY);
  1214.     }
  1215.     /**
  1216.      * Returns the SQL to subtract the number of given days to a date.
  1217.      *
  1218.      * @param string $date
  1219.      * @param int    $days
  1220.      *
  1221.      * @return string
  1222.      *
  1223.      * @throws Exception If not supported on this platform.
  1224.      */
  1225.     public function getDateSubDaysExpression($date$days)
  1226.     {
  1227.         return $this->getDateArithmeticIntervalExpression($date'-'$daysDateIntervalUnit::DAY);
  1228.     }
  1229.     /**
  1230.      * Returns the SQL to add the number of given weeks to a date.
  1231.      *
  1232.      * @param string $date
  1233.      * @param int    $weeks
  1234.      *
  1235.      * @return string
  1236.      *
  1237.      * @throws Exception If not supported on this platform.
  1238.      */
  1239.     public function getDateAddWeeksExpression($date$weeks)
  1240.     {
  1241.         return $this->getDateArithmeticIntervalExpression($date'+'$weeksDateIntervalUnit::WEEK);
  1242.     }
  1243.     /**
  1244.      * Returns the SQL to subtract the number of given weeks from a date.
  1245.      *
  1246.      * @param string $date
  1247.      * @param int    $weeks
  1248.      *
  1249.      * @return string
  1250.      *
  1251.      * @throws Exception If not supported on this platform.
  1252.      */
  1253.     public function getDateSubWeeksExpression($date$weeks)
  1254.     {
  1255.         return $this->getDateArithmeticIntervalExpression($date'-'$weeksDateIntervalUnit::WEEK);
  1256.     }
  1257.     /**
  1258.      * Returns the SQL to add the number of given months to a date.
  1259.      *
  1260.      * @param string $date
  1261.      * @param int    $months
  1262.      *
  1263.      * @return string
  1264.      *
  1265.      * @throws Exception If not supported on this platform.
  1266.      */
  1267.     public function getDateAddMonthExpression($date$months)
  1268.     {
  1269.         return $this->getDateArithmeticIntervalExpression($date'+'$monthsDateIntervalUnit::MONTH);
  1270.     }
  1271.     /**
  1272.      * Returns the SQL to subtract the number of given months to a date.
  1273.      *
  1274.      * @param string $date
  1275.      * @param int    $months
  1276.      *
  1277.      * @return string
  1278.      *
  1279.      * @throws Exception If not supported on this platform.
  1280.      */
  1281.     public function getDateSubMonthExpression($date$months)
  1282.     {
  1283.         return $this->getDateArithmeticIntervalExpression($date'-'$monthsDateIntervalUnit::MONTH);
  1284.     }
  1285.     /**
  1286.      * Returns the SQL to add the number of given quarters to a date.
  1287.      *
  1288.      * @param string $date
  1289.      * @param int    $quarters
  1290.      *
  1291.      * @return string
  1292.      *
  1293.      * @throws Exception If not supported on this platform.
  1294.      */
  1295.     public function getDateAddQuartersExpression($date$quarters)
  1296.     {
  1297.         return $this->getDateArithmeticIntervalExpression($date'+'$quartersDateIntervalUnit::QUARTER);
  1298.     }
  1299.     /**
  1300.      * Returns the SQL to subtract the number of given quarters from a date.
  1301.      *
  1302.      * @param string $date
  1303.      * @param int    $quarters
  1304.      *
  1305.      * @return string
  1306.      *
  1307.      * @throws Exception If not supported on this platform.
  1308.      */
  1309.     public function getDateSubQuartersExpression($date$quarters)
  1310.     {
  1311.         return $this->getDateArithmeticIntervalExpression($date'-'$quartersDateIntervalUnit::QUARTER);
  1312.     }
  1313.     /**
  1314.      * Returns the SQL to add the number of given years to a date.
  1315.      *
  1316.      * @param string $date
  1317.      * @param int    $years
  1318.      *
  1319.      * @return string
  1320.      *
  1321.      * @throws Exception If not supported on this platform.
  1322.      */
  1323.     public function getDateAddYearsExpression($date$years)
  1324.     {
  1325.         return $this->getDateArithmeticIntervalExpression($date'+'$yearsDateIntervalUnit::YEAR);
  1326.     }
  1327.     /**
  1328.      * Returns the SQL to subtract the number of given years from a date.
  1329.      *
  1330.      * @param string $date
  1331.      * @param int    $years
  1332.      *
  1333.      * @return string
  1334.      *
  1335.      * @throws Exception If not supported on this platform.
  1336.      */
  1337.     public function getDateSubYearsExpression($date$years)
  1338.     {
  1339.         return $this->getDateArithmeticIntervalExpression($date'-'$yearsDateIntervalUnit::YEAR);
  1340.     }
  1341.     /**
  1342.      * Returns the SQL for a date arithmetic expression.
  1343.      *
  1344.      * @param string $date     The column or literal representing a date to perform the arithmetic operation on.
  1345.      * @param string $operator The arithmetic operator (+ or -).
  1346.      * @param int    $interval The interval that shall be calculated into the date.
  1347.      * @param string $unit     The unit of the interval that shall be calculated into the date.
  1348.      *                         One of the DATE_INTERVAL_UNIT_* constants.
  1349.      *
  1350.      * @return string
  1351.      *
  1352.      * @throws Exception If not supported on this platform.
  1353.      */
  1354.     protected function getDateArithmeticIntervalExpression($date$operator$interval$unit)
  1355.     {
  1356.         throw Exception::notSupported(__METHOD__);
  1357.     }
  1358.     /**
  1359.      * Returns the SQL bit AND comparison expression.
  1360.      *
  1361.      * @param string $value1
  1362.      * @param string $value2
  1363.      *
  1364.      * @return string
  1365.      */
  1366.     public function getBitAndComparisonExpression($value1$value2)
  1367.     {
  1368.         return '(' $value1 ' & ' $value2 ')';
  1369.     }
  1370.     /**
  1371.      * Returns the SQL bit OR comparison expression.
  1372.      *
  1373.      * @param string $value1
  1374.      * @param string $value2
  1375.      *
  1376.      * @return string
  1377.      */
  1378.     public function getBitOrComparisonExpression($value1$value2)
  1379.     {
  1380.         return '(' $value1 ' | ' $value2 ')';
  1381.     }
  1382.     /**
  1383.      * Returns the SQL expression which represents the currently selected database.
  1384.      */
  1385.     abstract public function getCurrentDatabaseExpression(): string;
  1386.     /**
  1387.      * Returns the FOR UPDATE expression.
  1388.      *
  1389.      * @return string
  1390.      */
  1391.     public function getForUpdateSQL()
  1392.     {
  1393.         return 'FOR UPDATE';
  1394.     }
  1395.     /**
  1396.      * Honors that some SQL vendors such as MsSql use table hints for locking instead of the
  1397.      * ANSI SQL FOR UPDATE specification.
  1398.      *
  1399.      * @param string $fromClause The FROM clause to append the hint for the given lock mode to
  1400.      * @param int    $lockMode   One of the Doctrine\DBAL\LockMode::* constants
  1401.      * @psalm-param LockMode::* $lockMode
  1402.      */
  1403.     public function appendLockHint(string $fromClauseint $lockMode): string
  1404.     {
  1405.         switch ($lockMode) {
  1406.             case LockMode::NONE:
  1407.             case LockMode::OPTIMISTIC:
  1408.             case LockMode::PESSIMISTIC_READ:
  1409.             case LockMode::PESSIMISTIC_WRITE:
  1410.                 return $fromClause;
  1411.             default:
  1412.                 throw InvalidLockMode::fromLockMode($lockMode);
  1413.         }
  1414.     }
  1415.     /**
  1416.      * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock.
  1417.      *
  1418.      * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database
  1419.      * vendors allow to lighten this constraint up to be a real read lock.
  1420.      *
  1421.      * @return string
  1422.      */
  1423.     public function getReadLockSQL()
  1424.     {
  1425.         return $this->getForUpdateSQL();
  1426.     }
  1427.     /**
  1428.      * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows.
  1429.      *
  1430.      * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard.
  1431.      *
  1432.      * @return string
  1433.      */
  1434.     public function getWriteLockSQL()
  1435.     {
  1436.         return $this->getForUpdateSQL();
  1437.     }
  1438.     /**
  1439.      * Returns the SQL snippet to drop an existing table.
  1440.      *
  1441.      * @param Table|string $table
  1442.      *
  1443.      * @return string
  1444.      *
  1445.      * @throws InvalidArgumentException
  1446.      */
  1447.     public function getDropTableSQL($table)
  1448.     {
  1449.         $tableArg $table;
  1450.         if ($table instanceof Table) {
  1451.             $table $table->getQuotedName($this);
  1452.         }
  1453.         if (! is_string($table)) {
  1454.             throw new InvalidArgumentException(
  1455.                 __METHOD__ '() expects $table parameter to be string or ' Table::class . '.'
  1456.             );
  1457.         }
  1458.         if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) {
  1459.             $eventArgs = new SchemaDropTableEventArgs($tableArg$this);
  1460.             $this->_eventManager->dispatchEvent(Events::onSchemaDropTable$eventArgs);
  1461.             if ($eventArgs->isDefaultPrevented()) {
  1462.                 $sql $eventArgs->getSql();
  1463.                 if ($sql === null) {
  1464.                     throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL');
  1465.                 }
  1466.                 return $sql;
  1467.             }
  1468.         }
  1469.         return 'DROP TABLE ' $table;
  1470.     }
  1471.     /**
  1472.      * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction.
  1473.      *
  1474.      * @param Table|string $table
  1475.      *
  1476.      * @return string
  1477.      */
  1478.     public function getDropTemporaryTableSQL($table)
  1479.     {
  1480.         return $this->getDropTableSQL($table);
  1481.     }
  1482.     /**
  1483.      * Returns the SQL to drop an index from a table.
  1484.      *
  1485.      * @param Index|string      $index
  1486.      * @param Table|string|null $table
  1487.      *
  1488.      * @return string
  1489.      *
  1490.      * @throws InvalidArgumentException
  1491.      */
  1492.     public function getDropIndexSQL($index$table null)
  1493.     {
  1494.         if ($index instanceof Index) {
  1495.             $index $index->getQuotedName($this);
  1496.         } elseif (! is_string($index)) {
  1497.             throw new InvalidArgumentException(
  1498.                 __METHOD__ '() expects $index parameter to be string or ' Index::class . '.'
  1499.             );
  1500.         }
  1501.         return 'DROP INDEX ' $index;
  1502.     }
  1503.     /**
  1504.      * Returns the SQL to drop a constraint.
  1505.      *
  1506.      * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.
  1507.      *
  1508.      * @param Constraint|string $constraint
  1509.      * @param Table|string      $table
  1510.      *
  1511.      * @return string
  1512.      */
  1513.     public function getDropConstraintSQL($constraint$table)
  1514.     {
  1515.         if (! $constraint instanceof Constraint) {
  1516.             $constraint = new Identifier($constraint);
  1517.         }
  1518.         if (! $table instanceof Table) {
  1519.             $table = new Identifier($table);
  1520.         }
  1521.         $constraint $constraint->getQuotedName($this);
  1522.         $table      $table->getQuotedName($this);
  1523.         return 'ALTER TABLE ' $table ' DROP CONSTRAINT ' $constraint;
  1524.     }
  1525.     /**
  1526.      * Returns the SQL to drop a foreign key.
  1527.      *
  1528.      * @param ForeignKeyConstraint|string $foreignKey
  1529.      * @param Table|string                $table
  1530.      *
  1531.      * @return string
  1532.      */
  1533.     public function getDropForeignKeySQL($foreignKey$table)
  1534.     {
  1535.         if (! $foreignKey instanceof ForeignKeyConstraint) {
  1536.             $foreignKey = new Identifier($foreignKey);
  1537.         }
  1538.         if (! $table instanceof Table) {
  1539.             $table = new Identifier($table);
  1540.         }
  1541.         $foreignKey $foreignKey->getQuotedName($this);
  1542.         $table      $table->getQuotedName($this);
  1543.         return 'ALTER TABLE ' $table ' DROP FOREIGN KEY ' $foreignKey;
  1544.     }
  1545.     /**
  1546.      * Returns the SQL to drop a unique constraint.
  1547.      */
  1548.     public function getDropUniqueConstraintSQL(string $namestring $tableName): string
  1549.     {
  1550.         return $this->getDropConstraintSQL($name$tableName);
  1551.     }
  1552.     /**
  1553.      * Returns the SQL statement(s) to create a table with the specified name, columns and constraints
  1554.      * on this platform.
  1555.      *
  1556.      * @param int $createFlags
  1557.      *
  1558.      * @return string[] The sequence of SQL statements.
  1559.      *
  1560.      * @throws Exception
  1561.      * @throws InvalidArgumentException
  1562.      */
  1563.     public function getCreateTableSQL(Table $table$createFlags self::CREATE_INDEXES)
  1564.     {
  1565.         if (! is_int($createFlags)) {
  1566.             throw new InvalidArgumentException(
  1567.                 'Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.'
  1568.             );
  1569.         }
  1570.         if (count($table->getColumns()) === 0) {
  1571.             throw Exception::noColumnsSpecifiedForTable($table->getName());
  1572.         }
  1573.         $tableName                    $table->getQuotedName($this);
  1574.         $options                      $table->getOptions();
  1575.         $options['uniqueConstraints'] = [];
  1576.         $options['indexes']           = [];
  1577.         $options['primary']           = [];
  1578.         if (($createFlags self::CREATE_INDEXES) > 0) {
  1579.             foreach ($table->getIndexes() as $index) {
  1580.                 if (! $index->isPrimary()) {
  1581.                     $options['indexes'][$index->getQuotedName($this)] = $index;
  1582.                     continue;
  1583.                 }
  1584.                 $options['primary']       = $index->getQuotedColumns($this);
  1585.                 $options['primary_index'] = $index;
  1586.             }
  1587.             foreach ($table->getUniqueConstraints() as $uniqueConstraint) {
  1588.                 $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint;
  1589.             }
  1590.         }
  1591.         if (($createFlags self::CREATE_FOREIGNKEYS) > 0) {
  1592.             $options['foreignKeys'] = [];
  1593.             foreach ($table->getForeignKeys() as $fkConstraint) {
  1594.                 $options['foreignKeys'][] = $fkConstraint;
  1595.             }
  1596.         }
  1597.         $columnSql = [];
  1598.         $columns   = [];
  1599.         foreach ($table->getColumns() as $column) {
  1600.             if (
  1601.                 $this->_eventManager !== null
  1602.                 && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)
  1603.             ) {
  1604.                 $eventArgs = new SchemaCreateTableColumnEventArgs($column$table$this);
  1605.                 $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn$eventArgs);
  1606.                 $columnSql array_merge($columnSql$eventArgs->getSql());
  1607.                 if ($eventArgs->isDefaultPrevented()) {
  1608.                     continue;
  1609.                 }
  1610.             }
  1611.             $columnData $this->columnToArray($column);
  1612.             if (in_array($column->getName(), $options['primary'], true)) {
  1613.                 $columnData['primary'] = true;
  1614.             }
  1615.             $columns[$columnData['name']] = $columnData;
  1616.         }
  1617.         if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
  1618.             $eventArgs = new SchemaCreateTableEventArgs($table$columns$options$this);
  1619.             $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable$eventArgs);
  1620.             if ($eventArgs->isDefaultPrevented()) {
  1621.                 return array_merge($eventArgs->getSql(), $columnSql);
  1622.             }
  1623.         }
  1624.         $sql $this->_getCreateTableSQL($tableName$columns$options);
  1625.         if ($this->supportsCommentOnStatement()) {
  1626.             if ($table->hasOption('comment')) {
  1627.                 $sql[] = $this->getCommentOnTableSQL($tableName$table->getOption('comment'));
  1628.             }
  1629.             foreach ($table->getColumns() as $column) {
  1630.                 $comment $this->getColumnComment($column);
  1631.                 if ($comment === null || $comment === '') {
  1632.                     continue;
  1633.                 }
  1634.                 $sql[] = $this->getCommentOnColumnSQL($tableName$column->getQuotedName($this), $comment);
  1635.             }
  1636.         }
  1637.         return array_merge($sql$columnSql);
  1638.     }
  1639.     protected function getCommentOnTableSQL(string $tableName, ?string $comment): string
  1640.     {
  1641.         $tableName = new Identifier($tableName);
  1642.         return sprintf(
  1643.             'COMMENT ON TABLE %s IS %s',
  1644.             $tableName->getQuotedName($this),
  1645.             $this->quoteStringLiteral((string) $comment)
  1646.         );
  1647.     }
  1648.     /**
  1649.      * @param string      $tableName
  1650.      * @param string      $columnName
  1651.      * @param string|null $comment
  1652.      *
  1653.      * @return string
  1654.      */
  1655.     public function getCommentOnColumnSQL($tableName$columnName$comment)
  1656.     {
  1657.         $tableName  = new Identifier($tableName);
  1658.         $columnName = new Identifier($columnName);
  1659.         return sprintf(
  1660.             'COMMENT ON COLUMN %s.%s IS %s',
  1661.             $tableName->getQuotedName($this),
  1662.             $columnName->getQuotedName($this),
  1663.             $this->quoteStringLiteral((string) $comment)
  1664.         );
  1665.     }
  1666.     /**
  1667.      * Returns the SQL to create inline comment on a column.
  1668.      *
  1669.      * @param string $comment
  1670.      *
  1671.      * @return string
  1672.      *
  1673.      * @throws Exception If not supported on this platform.
  1674.      */
  1675.     public function getInlineColumnCommentSQL($comment)
  1676.     {
  1677.         if (! $this->supportsInlineColumnComments()) {
  1678.             throw Exception::notSupported(__METHOD__);
  1679.         }
  1680.         return 'COMMENT ' $this->quoteStringLiteral($comment);
  1681.     }
  1682.     /**
  1683.      * Returns the SQL used to create a table.
  1684.      *
  1685.      * @param string    $name
  1686.      * @param mixed[][] $columns
  1687.      * @param mixed[]   $options
  1688.      *
  1689.      * @return string[]
  1690.      */
  1691.     protected function _getCreateTableSQL($name, array $columns, array $options = [])
  1692.     {
  1693.         $columnListSql $this->getColumnDeclarationListSQL($columns);
  1694.         if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
  1695.             foreach ($options['uniqueConstraints'] as $index => $definition) {
  1696.                 $columnListSql .= ', ' $this->getUniqueConstraintDeclarationSQL($index$definition);
  1697.             }
  1698.         }
  1699.         if (isset($options['primary']) && ! empty($options['primary'])) {
  1700.             $columnListSql .= ', PRIMARY KEY(' implode(', 'array_unique(array_values($options['primary']))) . ')';
  1701.         }
  1702.         if (isset($options['indexes']) && ! empty($options['indexes'])) {
  1703.             foreach ($options['indexes'] as $index => $definition) {
  1704.                 $columnListSql .= ', ' $this->getIndexDeclarationSQL($index$definition);
  1705.             }
  1706.         }
  1707.         $query 'CREATE TABLE ' $name ' (' $columnListSql;
  1708.         $check $this->getCheckDeclarationSQL($columns);
  1709.         if (! empty($check)) {
  1710.             $query .= ', ' $check;
  1711.         }
  1712.         $query .= ')';
  1713.         $sql = [$query];
  1714.         if (isset($options['foreignKeys'])) {
  1715.             foreach ((array) $options['foreignKeys'] as $definition) {
  1716.                 $sql[] = $this->getCreateForeignKeySQL($definition$name);
  1717.             }
  1718.         }
  1719.         return $sql;
  1720.     }
  1721.     /**
  1722.      * @return string
  1723.      */
  1724.     public function getCreateTemporaryTableSnippetSQL()
  1725.     {
  1726.         return 'CREATE TEMPORARY TABLE';
  1727.     }
  1728.     /**
  1729.      * Returns the SQL to create a sequence on this platform.
  1730.      *
  1731.      * @return string
  1732.      *
  1733.      * @throws Exception If not supported on this platform.
  1734.      */
  1735.     public function getCreateSequenceSQL(Sequence $sequence)
  1736.     {
  1737.         throw Exception::notSupported(__METHOD__);
  1738.     }
  1739.     /**
  1740.      * Returns the SQL to change a sequence on this platform.
  1741.      *
  1742.      * @return string
  1743.      *
  1744.      * @throws Exception If not supported on this platform.
  1745.      */
  1746.     public function getAlterSequenceSQL(Sequence $sequence)
  1747.     {
  1748.         throw Exception::notSupported(__METHOD__);
  1749.     }
  1750.     /**
  1751.      * Returns the SQL snippet to drop an existing sequence.
  1752.      *
  1753.      * @param Sequence|string $sequence
  1754.      *
  1755.      * @return string
  1756.      *
  1757.      * @throws Exception If not supported on this platform.
  1758.      */
  1759.     public function getDropSequenceSQL($sequence)
  1760.     {
  1761.         if (! $this->supportsSequences()) {
  1762.             throw Exception::notSupported(__METHOD__);
  1763.         }
  1764.         if ($sequence instanceof Sequence) {
  1765.             $sequence $sequence->getQuotedName($this);
  1766.         }
  1767.         return 'DROP SEQUENCE ' $sequence;
  1768.     }
  1769.     /**
  1770.      * Returns the SQL to create a constraint on a table on this platform.
  1771.      *
  1772.      * @deprecated Use {@see getCreateIndexSQL()}, {@see getCreateForeignKeySQL()}
  1773.      *             or {@see getCreateUniqueConstraintSQL()} instead.
  1774.      *
  1775.      * @param Table|string $table
  1776.      *
  1777.      * @return string
  1778.      *
  1779.      * @throws InvalidArgumentException
  1780.      */
  1781.     public function getCreateConstraintSQL(Constraint $constraint$table)
  1782.     {
  1783.         if ($table instanceof Table) {
  1784.             $table $table->getQuotedName($this);
  1785.         }
  1786.         $query 'ALTER TABLE ' $table ' ADD CONSTRAINT ' $constraint->getQuotedName($this);
  1787.         $columnList '(' implode(', '$constraint->getQuotedColumns($this)) . ')';
  1788.         $referencesClause '';
  1789.         if ($constraint instanceof Index) {
  1790.             if ($constraint->isPrimary()) {
  1791.                 $query .= ' PRIMARY KEY';
  1792.             } elseif ($constraint->isUnique()) {
  1793.                 $query .= ' UNIQUE';
  1794.             } else {
  1795.                 throw new InvalidArgumentException(
  1796.                     'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().'
  1797.                 );
  1798.             }
  1799.         } elseif ($constraint instanceof UniqueConstraint) {
  1800.             $query .= ' UNIQUE';
  1801.         } elseif ($constraint instanceof ForeignKeyConstraint) {
  1802.             $query .= ' FOREIGN KEY';
  1803.             $referencesClause ' REFERENCES ' $constraint->getQuotedForeignTableName($this) .
  1804.                 ' (' implode(', '$constraint->getQuotedForeignColumns($this)) . ')';
  1805.         }
  1806.         $query .= ' ' $columnList $referencesClause;
  1807.         return $query;
  1808.     }
  1809.     /**
  1810.      * Returns the SQL to create an index on a table on this platform.
  1811.      *
  1812.      * @param Table|string $table The name of the table on which the index is to be created.
  1813.      *
  1814.      * @return string
  1815.      *
  1816.      * @throws InvalidArgumentException
  1817.      */
  1818.     public function getCreateIndexSQL(Index $index$table)
  1819.     {
  1820.         if ($table instanceof Table) {
  1821.             $table $table->getQuotedName($this);
  1822.         }
  1823.         $name    $index->getQuotedName($this);
  1824.         $columns $index->getColumns();
  1825.         if (count($columns) === 0) {
  1826.             throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
  1827.         }
  1828.         if ($index->isPrimary()) {
  1829.             return $this->getCreatePrimaryKeySQL($index$table);
  1830.         }
  1831.         $query  'CREATE ' $this->getCreateIndexSQLFlags($index) . 'INDEX ' $name ' ON ' $table;
  1832.         $query .= ' (' $this->getIndexFieldDeclarationListSQL($index) . ')' $this->getPartialIndexSQL($index);
  1833.         return $query;
  1834.     }
  1835.     /**
  1836.      * Adds condition for partial index.
  1837.      *
  1838.      * @return string
  1839.      */
  1840.     protected function getPartialIndexSQL(Index $index)
  1841.     {
  1842.         if ($this->supportsPartialIndexes() && $index->hasOption('where')) {
  1843.             return ' WHERE ' $index->getOption('where');
  1844.         }
  1845.         return '';
  1846.     }
  1847.     /**
  1848.      * Adds additional flags for index generation.
  1849.      *
  1850.      * @return string
  1851.      */
  1852.     protected function getCreateIndexSQLFlags(Index $index)
  1853.     {
  1854.         return $index->isUnique() ? 'UNIQUE ' '';
  1855.     }
  1856.     /**
  1857.      * Returns the SQL to create an unnamed primary key constraint.
  1858.      *
  1859.      * @param Table|string $table
  1860.      *
  1861.      * @return string
  1862.      */
  1863.     public function getCreatePrimaryKeySQL(Index $index$table)
  1864.     {
  1865.         if ($table instanceof Table) {
  1866.             $table $table->getQuotedName($this);
  1867.         }
  1868.         return 'ALTER TABLE ' $table ' ADD PRIMARY KEY (' $this->getIndexFieldDeclarationListSQL($index) . ')';
  1869.     }
  1870.     /**
  1871.      * Returns the SQL to create a named schema.
  1872.      *
  1873.      * @param string $schemaName
  1874.      *
  1875.      * @return string
  1876.      *
  1877.      * @throws Exception If not supported on this platform.
  1878.      */
  1879.     public function getCreateSchemaSQL($schemaName)
  1880.     {
  1881.         if (! $this->supportsSchemas()) {
  1882.             throw Exception::notSupported(__METHOD__);
  1883.         }
  1884.         return 'CREATE SCHEMA ' $schemaName;
  1885.     }
  1886.     /**
  1887.      * Returns the SQL to create a unique constraint on a table on this platform.
  1888.      */
  1889.     public function getCreateUniqueConstraintSQL(UniqueConstraint $constraintstring $tableName): string
  1890.     {
  1891.         return $this->getCreateConstraintSQL($constraint$tableName);
  1892.     }
  1893.     /**
  1894.      * Returns the SQL snippet to drop a schema.
  1895.      *
  1896.      * @throws Exception If not supported on this platform.
  1897.      */
  1898.     public function getDropSchemaSQL(string $schemaName): string
  1899.     {
  1900.         if (! $this->supportsSchemas()) {
  1901.             throw Exception::notSupported(__METHOD__);
  1902.         }
  1903.         return 'DROP SCHEMA ' $schemaName;
  1904.     }
  1905.     /**
  1906.      * Quotes a string so that it can be safely used as a table or column name,
  1907.      * even if it is a reserved word of the platform. This also detects identifier
  1908.      * chains separated by dot and quotes them independently.
  1909.      *
  1910.      * NOTE: Just because you CAN use quoted identifiers doesn't mean
  1911.      * you SHOULD use them. In general, they end up causing way more
  1912.      * problems than they solve.
  1913.      *
  1914.      * @param string $str The identifier name to be quoted.
  1915.      *
  1916.      * @return string The quoted identifier string.
  1917.      */
  1918.     public function quoteIdentifier($str)
  1919.     {
  1920.         if (strpos($str'.') !== false) {
  1921.             $parts array_map([$this'quoteSingleIdentifier'], explode('.'$str));
  1922.             return implode('.'$parts);
  1923.         }
  1924.         return $this->quoteSingleIdentifier($str);
  1925.     }
  1926.     /**
  1927.      * Quotes a single identifier (no dot chain separation).
  1928.      *
  1929.      * @param string $str The identifier name to be quoted.
  1930.      *
  1931.      * @return string The quoted identifier string.
  1932.      */
  1933.     public function quoteSingleIdentifier($str)
  1934.     {
  1935.         $c $this->getIdentifierQuoteCharacter();
  1936.         return $c str_replace($c$c $c$str) . $c;
  1937.     }
  1938.     /**
  1939.      * Returns the SQL to create a new foreign key.
  1940.      *
  1941.      * @param ForeignKeyConstraint $foreignKey The foreign key constraint.
  1942.      * @param Table|string         $table      The name of the table on which the foreign key is to be created.
  1943.      *
  1944.      * @return string
  1945.      */
  1946.     public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey$table)
  1947.     {
  1948.         if ($table instanceof Table) {
  1949.             $table $table->getQuotedName($this);
  1950.         }
  1951.         return 'ALTER TABLE ' $table ' ADD ' $this->getForeignKeyDeclarationSQL($foreignKey);
  1952.     }
  1953.     /**
  1954.      * Gets the SQL statements for altering an existing table.
  1955.      *
  1956.      * This method returns an array of SQL statements, since some platforms need several statements.
  1957.      *
  1958.      * @return string[]
  1959.      *
  1960.      * @throws Exception If not supported on this platform.
  1961.      */
  1962.     public function getAlterTableSQL(TableDiff $diff)
  1963.     {
  1964.         throw Exception::notSupported(__METHOD__);
  1965.     }
  1966.     /**
  1967.      * @param mixed[] $columnSql
  1968.      *
  1969.      * @return bool
  1970.      */
  1971.     protected function onSchemaAlterTableAddColumn(Column $columnTableDiff $diff, &$columnSql)
  1972.     {
  1973.         if ($this->_eventManager === null) {
  1974.             return false;
  1975.         }
  1976.         if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) {
  1977.             return false;
  1978.         }
  1979.         $eventArgs = new SchemaAlterTableAddColumnEventArgs($column$diff$this);
  1980.         $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn$eventArgs);
  1981.         $columnSql array_merge($columnSql$eventArgs->getSql());
  1982.         return $eventArgs->isDefaultPrevented();
  1983.     }
  1984.     /**
  1985.      * @param string[] $columnSql
  1986.      *
  1987.      * @return bool
  1988.      */
  1989.     protected function onSchemaAlterTableRemoveColumn(Column $columnTableDiff $diff, &$columnSql)
  1990.     {
  1991.         if ($this->_eventManager === null) {
  1992.             return false;
  1993.         }
  1994.         if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) {
  1995.             return false;
  1996.         }
  1997.         $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column$diff$this);
  1998.         $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn$eventArgs);
  1999.         $columnSql array_merge($columnSql$eventArgs->getSql());
  2000.         return $eventArgs->isDefaultPrevented();
  2001.     }
  2002.     /**
  2003.      * @param string[] $columnSql
  2004.      *
  2005.      * @return bool
  2006.      */
  2007.     protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiffTableDiff $diff, &$columnSql)
  2008.     {
  2009.         if ($this->_eventManager === null) {
  2010.             return false;
  2011.         }
  2012.         if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) {
  2013.             return false;
  2014.         }
  2015.         $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff$diff$this);
  2016.         $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn$eventArgs);
  2017.         $columnSql array_merge($columnSql$eventArgs->getSql());
  2018.         return $eventArgs->isDefaultPrevented();
  2019.     }
  2020.     /**
  2021.      * @param string   $oldColumnName
  2022.      * @param string[] $columnSql
  2023.      *
  2024.      * @return bool
  2025.      */
  2026.     protected function onSchemaAlterTableRenameColumn($oldColumnNameColumn $columnTableDiff $diff, &$columnSql)
  2027.     {
  2028.         if ($this->_eventManager === null) {
  2029.             return false;
  2030.         }
  2031.         if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) {
  2032.             return false;
  2033.         }
  2034.         $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName$column$diff$this);
  2035.         $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn$eventArgs);
  2036.         $columnSql array_merge($columnSql$eventArgs->getSql());
  2037.         return $eventArgs->isDefaultPrevented();
  2038.     }
  2039.     /**
  2040.      * @param string[] $sql
  2041.      *
  2042.      * @return bool
  2043.      */
  2044.     protected function onSchemaAlterTable(TableDiff $diff, &$sql)
  2045.     {
  2046.         if ($this->_eventManager === null) {
  2047.             return false;
  2048.         }
  2049.         if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) {
  2050.             return false;
  2051.         }
  2052.         $eventArgs = new SchemaAlterTableEventArgs($diff$this);
  2053.         $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable$eventArgs);
  2054.         $sql array_merge($sql$eventArgs->getSql());
  2055.         return $eventArgs->isDefaultPrevented();
  2056.     }
  2057.     /**
  2058.      * @return string[]
  2059.      */
  2060.     protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
  2061.     {
  2062.         $tableName $diff->getName($this)->getQuotedName($this);
  2063.         $sql = [];
  2064.         if ($this->supportsForeignKeyConstraints()) {
  2065.             foreach ($diff->removedForeignKeys as $foreignKey) {
  2066.                 $sql[] = $this->getDropForeignKeySQL($foreignKey$tableName);
  2067.             }
  2068.             foreach ($diff->changedForeignKeys as $foreignKey) {
  2069.                 $sql[] = $this->getDropForeignKeySQL($foreignKey$tableName);
  2070.             }
  2071.         }
  2072.         foreach ($diff->removedIndexes as $index) {
  2073.             $sql[] = $this->getDropIndexSQL($index$tableName);
  2074.         }
  2075.         foreach ($diff->changedIndexes as $index) {
  2076.             $sql[] = $this->getDropIndexSQL($index$tableName);
  2077.         }
  2078.         return $sql;
  2079.     }
  2080.     /**
  2081.      * @return string[]
  2082.      */
  2083.     protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
  2084.     {
  2085.         $sql     = [];
  2086.         $newName $diff->getNewName();
  2087.         if ($newName !== false) {
  2088.             $tableName $newName->getQuotedName($this);
  2089.         } else {
  2090.             $tableName $diff->getName($this)->getQuotedName($this);
  2091.         }
  2092.         if ($this->supportsForeignKeyConstraints()) {
  2093.             foreach ($diff->addedForeignKeys as $foreignKey) {
  2094.                 $sql[] = $this->getCreateForeignKeySQL($foreignKey$tableName);
  2095.             }
  2096.             foreach ($diff->changedForeignKeys as $foreignKey) {
  2097.                 $sql[] = $this->getCreateForeignKeySQL($foreignKey$tableName);
  2098.             }
  2099.         }
  2100.         foreach ($diff->addedIndexes as $index) {
  2101.             $sql[] = $this->getCreateIndexSQL($index$tableName);
  2102.         }
  2103.         foreach ($diff->changedIndexes as $index) {
  2104.             $sql[] = $this->getCreateIndexSQL($index$tableName);
  2105.         }
  2106.         foreach ($diff->renamedIndexes as $oldIndexName => $index) {
  2107.             $oldIndexName = new Identifier($oldIndexName);
  2108.             $sql          array_merge(
  2109.                 $sql,
  2110.                 $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index$tableName)
  2111.             );
  2112.         }
  2113.         return $sql;
  2114.     }
  2115.     /**
  2116.      * Returns the SQL for renaming an index on a table.
  2117.      *
  2118.      * @param string $oldIndexName The name of the index to rename from.
  2119.      * @param Index  $index        The definition of the index to rename to.
  2120.      * @param string $tableName    The table to rename the given index on.
  2121.      *
  2122.      * @return string[] The sequence of SQL statements for renaming the given index.
  2123.      */
  2124.     protected function getRenameIndexSQL($oldIndexNameIndex $index$tableName)
  2125.     {
  2126.         return [
  2127.             $this->getDropIndexSQL($oldIndexName$tableName),
  2128.             $this->getCreateIndexSQL($index$tableName),
  2129.         ];
  2130.     }
  2131.     /**
  2132.      * Gets declaration of a number of columns in bulk.
  2133.      *
  2134.      * @param mixed[][] $columns A multidimensional associative array.
  2135.      *                           The first dimension determines the column name, while the second
  2136.      *                           dimension is keyed with the name of the properties
  2137.      *                           of the column being declared as array indexes. Currently, the types
  2138.      *                           of supported column properties are as follows:
  2139.      *
  2140.      *      length
  2141.      *          Integer value that determines the maximum length of the text
  2142.      *          column. If this argument is missing the column should be
  2143.      *          declared to have the longest length allowed by the DBMS.
  2144.      *
  2145.      *      default
  2146.      *          Text value to be used as default for this column.
  2147.      *
  2148.      *      notnull
  2149.      *          Boolean flag that indicates whether this column is constrained
  2150.      *          to not be set to null.
  2151.      *      charset
  2152.      *          Text value with the default CHARACTER SET for this column.
  2153.      *      collation
  2154.      *          Text value with the default COLLATION for this column.
  2155.      *      unique
  2156.      *          unique constraint
  2157.      *
  2158.      * @return string
  2159.      */
  2160.     public function getColumnDeclarationListSQL(array $columns)
  2161.     {
  2162.         $declarations = [];
  2163.         foreach ($columns as $name => $column) {
  2164.             $declarations[] = $this->getColumnDeclarationSQL($name$column);
  2165.         }
  2166.         return implode(', '$declarations);
  2167.     }
  2168.     /**
  2169.      * Obtains DBMS specific SQL code portion needed to declare a generic type
  2170.      * column to be used in statements like CREATE TABLE.
  2171.      *
  2172.      * @param string  $name   The name the column to be declared.
  2173.      * @param mixed[] $column An associative array with the name of the properties
  2174.      *                        of the column being declared as array indexes. Currently, the types
  2175.      *                        of supported column properties are as follows:
  2176.      *
  2177.      *      length
  2178.      *          Integer value that determines the maximum length of the text
  2179.      *          column. If this argument is missing the column should be
  2180.      *          declared to have the longest length allowed by the DBMS.
  2181.      *
  2182.      *      default
  2183.      *          Text value to be used as default for this column.
  2184.      *
  2185.      *      notnull
  2186.      *          Boolean flag that indicates whether this column is constrained
  2187.      *          to not be set to null.
  2188.      *      charset
  2189.      *          Text value with the default CHARACTER SET for this column.
  2190.      *      collation
  2191.      *          Text value with the default COLLATION for this column.
  2192.      *      unique
  2193.      *          unique constraint
  2194.      *      check
  2195.      *          column check constraint
  2196.      *      columnDefinition
  2197.      *          a string that defines the complete column
  2198.      *
  2199.      * @return string DBMS specific SQL code portion that should be used to declare the column.
  2200.      *
  2201.      * @throws Exception
  2202.      */
  2203.     public function getColumnDeclarationSQL($name, array $column)
  2204.     {
  2205.         if (isset($column['columnDefinition'])) {
  2206.             $declaration $this->getCustomTypeDeclarationSQL($column);
  2207.         } else {
  2208.             $default $this->getDefaultValueDeclarationSQL($column);
  2209.             $charset = ! empty($column['charset']) ?
  2210.                 ' ' $this->getColumnCharsetDeclarationSQL($column['charset']) : '';
  2211.             $collation = ! empty($column['collation']) ?
  2212.                 ' ' $this->getColumnCollationDeclarationSQL($column['collation']) : '';
  2213.             $notnull = ! empty($column['notnull']) ? ' NOT NULL' '';
  2214.             $unique = ! empty($column['unique']) ?
  2215.                 ' ' $this->getUniqueFieldDeclarationSQL() : '';
  2216.             $check = ! empty($column['check']) ? ' ' $column['check'] : '';
  2217.             $typeDecl    $column['type']->getSQLDeclaration($column$this);
  2218.             $declaration $typeDecl $charset $default $notnull $unique $check $collation;
  2219.             if ($this->supportsInlineColumnComments() && isset($column['comment']) && $column['comment'] !== '') {
  2220.                 $declaration .= ' ' $this->getInlineColumnCommentSQL($column['comment']);
  2221.             }
  2222.         }
  2223.         return $name ' ' $declaration;
  2224.     }
  2225.     /**
  2226.      * Returns the SQL snippet that declares a floating point column of arbitrary precision.
  2227.      *
  2228.      * @param mixed[] $column
  2229.      *
  2230.      * @return string
  2231.      */
  2232.     public function getDecimalTypeDeclarationSQL(array $column)
  2233.     {
  2234.         $column['precision'] = ! isset($column['precision']) || empty($column['precision'])
  2235.             ? 10 $column['precision'];
  2236.         $column['scale']     = ! isset($column['scale']) || empty($column['scale'])
  2237.             ? $column['scale'];
  2238.         return 'NUMERIC(' $column['precision'] . ', ' $column['scale'] . ')';
  2239.     }
  2240.     /**
  2241.      * Obtains DBMS specific SQL code portion needed to set a default value
  2242.      * declaration to be used in statements like CREATE TABLE.
  2243.      *
  2244.      * @param mixed[] $column The column definition array.
  2245.      *
  2246.      * @return string DBMS specific SQL code portion needed to set a default value.
  2247.      */
  2248.     public function getDefaultValueDeclarationSQL($column)
  2249.     {
  2250.         if (! isset($column['default'])) {
  2251.             return empty($column['notnull']) ? ' DEFAULT NULL' '';
  2252.         }
  2253.         $default $column['default'];
  2254.         if (! isset($column['type'])) {
  2255.             return " DEFAULT '" $default "'";
  2256.         }
  2257.         $type $column['type'];
  2258.         if ($type instanceof Types\PhpIntegerMappingType) {
  2259.             return ' DEFAULT ' $default;
  2260.         }
  2261.         if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
  2262.             return ' DEFAULT ' $this->getCurrentTimestampSQL();
  2263.         }
  2264.         if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
  2265.             return ' DEFAULT ' $this->getCurrentTimeSQL();
  2266.         }
  2267.         if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
  2268.             return ' DEFAULT ' $this->getCurrentDateSQL();
  2269.         }
  2270.         if ($type instanceof Types\BooleanType) {
  2271.             return ' DEFAULT ' $this->convertBooleans($default);
  2272.         }
  2273.         return ' DEFAULT ' $this->quoteStringLiteral($default);
  2274.     }
  2275.     /**
  2276.      * Obtains DBMS specific SQL code portion needed to set a CHECK constraint
  2277.      * declaration to be used in statements like CREATE TABLE.
  2278.      *
  2279.      * @param string[]|mixed[][] $definition The check definition.
  2280.      *
  2281.      * @return string DBMS specific SQL code portion needed to set a CHECK constraint.
  2282.      */
  2283.     public function getCheckDeclarationSQL(array $definition)
  2284.     {
  2285.         $constraints = [];
  2286.         foreach ($definition as $column => $def) {
  2287.             if (is_string($def)) {
  2288.                 $constraints[] = 'CHECK (' $def ')';
  2289.             } else {
  2290.                 if (isset($def['min'])) {
  2291.                     $constraints[] = 'CHECK (' $column ' >= ' $def['min'] . ')';
  2292.                 }
  2293.                 if (isset($def['max'])) {
  2294.                     $constraints[] = 'CHECK (' $column ' <= ' $def['max'] . ')';
  2295.                 }
  2296.             }
  2297.         }
  2298.         return implode(', '$constraints);
  2299.     }
  2300.     /**
  2301.      * Obtains DBMS specific SQL code portion needed to set a unique
  2302.      * constraint declaration to be used in statements like CREATE TABLE.
  2303.      *
  2304.      * @param string           $name       The name of the unique constraint.
  2305.      * @param UniqueConstraint $constraint The unique constraint definition.
  2306.      *
  2307.      * @return string DBMS specific SQL code portion needed to set a constraint.
  2308.      *
  2309.      * @throws InvalidArgumentException
  2310.      */
  2311.     public function getUniqueConstraintDeclarationSQL($nameUniqueConstraint $constraint)
  2312.     {
  2313.         $columns $constraint->getQuotedColumns($this);
  2314.         $name    = new Identifier($name);
  2315.         if (count($columns) === 0) {
  2316.             throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
  2317.         }
  2318.         $constraintFlags array_merge(['UNIQUE'], array_map('strtoupper'$constraint->getFlags()));
  2319.         $constraintName  $name->getQuotedName($this);
  2320.         $columnListNames $this->getColumnsFieldDeclarationListSQL($columns);
  2321.         return sprintf('CONSTRAINT %s %s (%s)'$constraintNameimplode(' '$constraintFlags), $columnListNames);
  2322.     }
  2323.     /**
  2324.      * Obtains DBMS specific SQL code portion needed to set an index
  2325.      * declaration to be used in statements like CREATE TABLE.
  2326.      *
  2327.      * @param string $name  The name of the index.
  2328.      * @param Index  $index The index definition.
  2329.      *
  2330.      * @return string DBMS specific SQL code portion needed to set an index.
  2331.      *
  2332.      * @throws InvalidArgumentException
  2333.      */
  2334.     public function getIndexDeclarationSQL($nameIndex $index)
  2335.     {
  2336.         $columns $index->getColumns();
  2337.         $name    = new Identifier($name);
  2338.         if (count($columns) === 0) {
  2339.             throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
  2340.         }
  2341.         return $this->getCreateIndexSQLFlags($index) . 'INDEX ' $name->getQuotedName($this)
  2342.             . ' (' $this->getIndexFieldDeclarationListSQL($index) . ')' $this->getPartialIndexSQL($index);
  2343.     }
  2344.     /**
  2345.      * Obtains SQL code portion needed to create a custom column,
  2346.      * e.g. when a column has the "columnDefinition" keyword.
  2347.      * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate.
  2348.      *
  2349.      * @param mixed[] $column
  2350.      *
  2351.      * @return string
  2352.      */
  2353.     public function getCustomTypeDeclarationSQL(array $column)
  2354.     {
  2355.         return $column['columnDefinition'];
  2356.     }
  2357.     /**
  2358.      * Obtains DBMS specific SQL code portion needed to set an index
  2359.      * declaration to be used in statements like CREATE TABLE.
  2360.      */
  2361.     public function getIndexFieldDeclarationListSQL(Index $index): string
  2362.     {
  2363.         return implode(', '$index->getQuotedColumns($this));
  2364.     }
  2365.     /**
  2366.      * Obtains DBMS specific SQL code portion needed to set an index
  2367.      * declaration to be used in statements like CREATE TABLE.
  2368.      *
  2369.      * @param mixed[] $columns
  2370.      */
  2371.     public function getColumnsFieldDeclarationListSQL(array $columns): string
  2372.     {
  2373.         $ret = [];
  2374.         foreach ($columns as $column => $definition) {
  2375.             if (is_array($definition)) {
  2376.                 $ret[] = $column;
  2377.             } else {
  2378.                 $ret[] = $definition;
  2379.             }
  2380.         }
  2381.         return implode(', '$ret);
  2382.     }
  2383.     /**
  2384.      * Returns the required SQL string that fits between CREATE ... TABLE
  2385.      * to create the table as a temporary table.
  2386.      *
  2387.      * Should be overridden in driver classes to return the correct string for the
  2388.      * specific database type.
  2389.      *
  2390.      * The default is to return the string "TEMPORARY" - this will result in a
  2391.      * SQL error for any database that does not support temporary tables, or that
  2392.      * requires a different SQL command from "CREATE TEMPORARY TABLE".
  2393.      *
  2394.      * @deprecated
  2395.      *
  2396.      * @return string The string required to be placed between "CREATE" and "TABLE"
  2397.      *                to generate a temporary table, if possible.
  2398.      */
  2399.     public function getTemporaryTableSQL()
  2400.     {
  2401.         Deprecation::trigger(
  2402.             'doctrine/dbal',
  2403.             'https://github.com/doctrine/dbal/pulls/4724',
  2404.             'AbstractPlatform::getTemporaryTableSQL() is deprecated.'
  2405.         );
  2406.         return 'TEMPORARY';
  2407.     }
  2408.     /**
  2409.      * Some vendors require temporary table names to be qualified specially.
  2410.      *
  2411.      * @param string $tableName
  2412.      *
  2413.      * @return string
  2414.      */
  2415.     public function getTemporaryTableName($tableName)
  2416.     {
  2417.         return $tableName;
  2418.     }
  2419.     /**
  2420.      * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
  2421.      * of a column declaration to be used in statements like CREATE TABLE.
  2422.      *
  2423.      * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
  2424.      *                of a column declaration.
  2425.      */
  2426.     public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey)
  2427.     {
  2428.         $sql  $this->getForeignKeyBaseDeclarationSQL($foreignKey);
  2429.         $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey);
  2430.         return $sql;
  2431.     }
  2432.     /**
  2433.      * Returns the FOREIGN KEY query section dealing with non-standard options
  2434.      * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
  2435.      *
  2436.      * @param ForeignKeyConstraint $foreignKey The foreign key definition.
  2437.      *
  2438.      * @return string
  2439.      */
  2440.     public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
  2441.     {
  2442.         $query '';
  2443.         if ($foreignKey->hasOption('onUpdate')) {
  2444.             $query .= ' ON UPDATE ' $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
  2445.         }
  2446.         if ($foreignKey->hasOption('onDelete')) {
  2447.             $query .= ' ON DELETE ' $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
  2448.         }
  2449.         return $query;
  2450.     }
  2451.     /**
  2452.      * Returns the given referential action in uppercase if valid, otherwise throws an exception.
  2453.      *
  2454.      * @param string $action The foreign key referential action.
  2455.      *
  2456.      * @return string
  2457.      *
  2458.      * @throws InvalidArgumentException If unknown referential action given.
  2459.      */
  2460.     public function getForeignKeyReferentialActionSQL($action)
  2461.     {
  2462.         $upper strtoupper($action);
  2463.         switch ($upper) {
  2464.             case 'CASCADE':
  2465.             case 'SET NULL':
  2466.             case 'NO ACTION':
  2467.             case 'RESTRICT':
  2468.             case 'SET DEFAULT':
  2469.                 return $upper;
  2470.             default:
  2471.                 throw new InvalidArgumentException('Invalid foreign key action: ' $upper);
  2472.         }
  2473.     }
  2474.     /**
  2475.      * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
  2476.      * of a column declaration to be used in statements like CREATE TABLE.
  2477.      *
  2478.      * @return string
  2479.      *
  2480.      * @throws InvalidArgumentException
  2481.      */
  2482.     public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
  2483.     {
  2484.         $sql '';
  2485.         if (strlen($foreignKey->getName()) > 0) {
  2486.             $sql .= 'CONSTRAINT ' $foreignKey->getQuotedName($this) . ' ';
  2487.         }
  2488.         $sql .= 'FOREIGN KEY (';
  2489.         if (count($foreignKey->getLocalColumns()) === 0) {
  2490.             throw new InvalidArgumentException("Incomplete definition. 'local' required.");
  2491.         }
  2492.         if (count($foreignKey->getForeignColumns()) === 0) {
  2493.             throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
  2494.         }
  2495.         if (strlen($foreignKey->getForeignTableName()) === 0) {
  2496.             throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
  2497.         }
  2498.         return $sql implode(', '$foreignKey->getQuotedLocalColumns($this))
  2499.             . ') REFERENCES '
  2500.             $foreignKey->getQuotedForeignTableName($this) . ' ('
  2501.             implode(', '$foreignKey->getQuotedForeignColumns($this)) . ')';
  2502.     }
  2503.     /**
  2504.      * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint
  2505.      * of a column declaration to be used in statements like CREATE TABLE.
  2506.      *
  2507.      * @deprecated Use UNIQUE in SQL instead.
  2508.      *
  2509.      * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
  2510.      *                of a column declaration.
  2511.      */
  2512.     public function getUniqueFieldDeclarationSQL()
  2513.     {
  2514.         Deprecation::trigger(
  2515.             'doctrine/dbal',
  2516.             'https://github.com/doctrine/dbal/pulls/4724',
  2517.             'AbstractPlatform::getUniqueFieldDeclarationSQL() is deprecated. Use UNIQUE in SQL instead.'
  2518.         );
  2519.         return 'UNIQUE';
  2520.     }
  2521.     /**
  2522.      * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET
  2523.      * of a column declaration to be used in statements like CREATE TABLE.
  2524.      *
  2525.      * @param string $charset The name of the charset.
  2526.      *
  2527.      * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
  2528.      *                of a column declaration.
  2529.      */
  2530.     public function getColumnCharsetDeclarationSQL($charset)
  2531.     {
  2532.         return '';
  2533.     }
  2534.     /**
  2535.      * Obtains DBMS specific SQL code portion needed to set the COLLATION
  2536.      * of a column declaration to be used in statements like CREATE TABLE.
  2537.      *
  2538.      * @param string $collation The name of the collation.
  2539.      *
  2540.      * @return string DBMS specific SQL code portion needed to set the COLLATION
  2541.      *                of a column declaration.
  2542.      */
  2543.     public function getColumnCollationDeclarationSQL($collation)
  2544.     {
  2545.         return $this->supportsColumnCollation() ? 'COLLATE ' $collation '';
  2546.     }
  2547.     /**
  2548.      * Whether the platform prefers identity columns (eg. autoincrement) for ID generation.
  2549.      * Subclasses should override this method to return TRUE if they prefer identity columns.
  2550.      *
  2551.      * @deprecated
  2552.      *
  2553.      * @return bool
  2554.      */
  2555.     public function prefersIdentityColumns()
  2556.     {
  2557.         Deprecation::trigger(
  2558.             'doctrine/dbal',
  2559.             'https://github.com/doctrine/dbal/pulls/1519',
  2560.             'AbstractPlatform::prefersIdentityColumns() is deprecated.'
  2561.         );
  2562.         return false;
  2563.     }
  2564.     /**
  2565.      * Some platforms need the boolean values to be converted.
  2566.      *
  2567.      * The default conversion in this implementation converts to integers (false => 0, true => 1).
  2568.      *
  2569.      * Note: if the input is not a boolean the original input might be returned.
  2570.      *
  2571.      * There are two contexts when converting booleans: Literals and Prepared Statements.
  2572.      * This method should handle the literal case
  2573.      *
  2574.      * @param mixed $item A boolean or an array of them.
  2575.      *
  2576.      * @return mixed A boolean database value or an array of them.
  2577.      */
  2578.     public function convertBooleans($item)
  2579.     {
  2580.         if (is_array($item)) {
  2581.             foreach ($item as $k => $value) {
  2582.                 if (! is_bool($value)) {
  2583.                     continue;
  2584.                 }
  2585.                 $item[$k] = (int) $value;
  2586.             }
  2587.         } elseif (is_bool($item)) {
  2588.             $item = (int) $item;
  2589.         }
  2590.         return $item;
  2591.     }
  2592.     /**
  2593.      * Some platforms have boolean literals that needs to be correctly converted
  2594.      *
  2595.      * The default conversion tries to convert value into bool "(bool)$item"
  2596.      *
  2597.      * @param mixed $item
  2598.      *
  2599.      * @return bool|null
  2600.      */
  2601.     public function convertFromBoolean($item)
  2602.     {
  2603.         return $item === null null : (bool) $item;
  2604.     }
  2605.     /**
  2606.      * This method should handle the prepared statements case. When there is no
  2607.      * distinction, it's OK to use the same method.
  2608.      *
  2609.      * Note: if the input is not a boolean the original input might be returned.
  2610.      *
  2611.      * @param mixed $item A boolean or an array of them.
  2612.      *
  2613.      * @return mixed A boolean database value or an array of them.
  2614.      */
  2615.     public function convertBooleansToDatabaseValue($item)
  2616.     {
  2617.         return $this->convertBooleans($item);
  2618.     }
  2619.     /**
  2620.      * Returns the SQL specific for the platform to get the current date.
  2621.      *
  2622.      * @return string
  2623.      */
  2624.     public function getCurrentDateSQL()
  2625.     {
  2626.         return 'CURRENT_DATE';
  2627.     }
  2628.     /**
  2629.      * Returns the SQL specific for the platform to get the current time.
  2630.      *
  2631.      * @return string
  2632.      */
  2633.     public function getCurrentTimeSQL()
  2634.     {
  2635.         return 'CURRENT_TIME';
  2636.     }
  2637.     /**
  2638.      * Returns the SQL specific for the platform to get the current timestamp
  2639.      *
  2640.      * @return string
  2641.      */
  2642.     public function getCurrentTimestampSQL()
  2643.     {
  2644.         return 'CURRENT_TIMESTAMP';
  2645.     }
  2646.     /**
  2647.      * Returns the SQL for a given transaction isolation level Connection constant.
  2648.      *
  2649.      * @param int $level
  2650.      *
  2651.      * @return string
  2652.      *
  2653.      * @throws InvalidArgumentException
  2654.      */
  2655.     protected function _getTransactionIsolationLevelSQL($level)
  2656.     {
  2657.         switch ($level) {
  2658.             case TransactionIsolationLevel::READ_UNCOMMITTED:
  2659.                 return 'READ UNCOMMITTED';
  2660.             case TransactionIsolationLevel::READ_COMMITTED:
  2661.                 return 'READ COMMITTED';
  2662.             case TransactionIsolationLevel::REPEATABLE_READ:
  2663.                 return 'REPEATABLE READ';
  2664.             case TransactionIsolationLevel::SERIALIZABLE:
  2665.                 return 'SERIALIZABLE';
  2666.             default:
  2667.                 throw new InvalidArgumentException('Invalid isolation level:' $level);
  2668.         }
  2669.     }
  2670.     /**
  2671.      * @return string
  2672.      *
  2673.      * @throws Exception If not supported on this platform.
  2674.      */
  2675.     public function getListDatabasesSQL()
  2676.     {
  2677.         throw Exception::notSupported(__METHOD__);
  2678.     }
  2679.     /**
  2680.      * Returns the SQL statement for retrieving the namespaces defined in the database.
  2681.      *
  2682.      * @deprecated Use {@see AbstractSchemaManager::listSchemaNames()} instead.
  2683.      *
  2684.      * @return string
  2685.      *
  2686.      * @throws Exception If not supported on this platform.
  2687.      */
  2688.     public function getListNamespacesSQL()
  2689.     {
  2690.         Deprecation::triggerIfCalledFromOutside(
  2691.             'doctrine/dbal',
  2692.             'https://github.com/doctrine/dbal/issues/4503',
  2693.             'AbstractPlatform::getListNamespacesSQL() is deprecated,'
  2694.                 ' use AbstractSchemaManager::listSchemaNames() instead.'
  2695.         );
  2696.         throw Exception::notSupported(__METHOD__);
  2697.     }
  2698.     /**
  2699.      * @param string $database
  2700.      *
  2701.      * @return string
  2702.      *
  2703.      * @throws Exception If not supported on this platform.
  2704.      */
  2705.     public function getListSequencesSQL($database)
  2706.     {
  2707.         throw Exception::notSupported(__METHOD__);
  2708.     }
  2709.     /**
  2710.      * @param string $table
  2711.      *
  2712.      * @return string
  2713.      *
  2714.      * @throws Exception If not supported on this platform.
  2715.      */
  2716.     public function getListTableConstraintsSQL($table)
  2717.     {
  2718.         throw Exception::notSupported(__METHOD__);
  2719.     }
  2720.     /**
  2721.      * @param string $table
  2722.      * @param string $database
  2723.      *
  2724.      * @return string
  2725.      *
  2726.      * @throws Exception If not supported on this platform.
  2727.      */
  2728.     public function getListTableColumnsSQL($table$database null)
  2729.     {
  2730.         throw Exception::notSupported(__METHOD__);
  2731.     }
  2732.     /**
  2733.      * @return string
  2734.      *
  2735.      * @throws Exception If not supported on this platform.
  2736.      */
  2737.     public function getListTablesSQL()
  2738.     {
  2739.         throw Exception::notSupported(__METHOD__);
  2740.     }
  2741.     /**
  2742.      * @deprecated
  2743.      *
  2744.      * @return string
  2745.      *
  2746.      * @throws Exception If not supported on this platform.
  2747.      */
  2748.     public function getListUsersSQL()
  2749.     {
  2750.         Deprecation::trigger(
  2751.             'doctrine/dbal',
  2752.             'https://github.com/doctrine/dbal/pulls/4724',
  2753.             'AbstractPlatform::getListUsersSQL() is deprecated.'
  2754.         );
  2755.         throw Exception::notSupported(__METHOD__);
  2756.     }
  2757.     /**
  2758.      * Returns the SQL to list all views of a database or user.
  2759.      *
  2760.      * @param string $database
  2761.      *
  2762.      * @return string
  2763.      *
  2764.      * @throws Exception If not supported on this platform.
  2765.      */
  2766.     public function getListViewsSQL($database)
  2767.     {
  2768.         throw Exception::notSupported(__METHOD__);
  2769.     }
  2770.     /**
  2771.      * Returns the list of indexes for the current database.
  2772.      *
  2773.      * The current database parameter is optional but will always be passed
  2774.      * when using the SchemaManager API and is the database the given table is in.
  2775.      *
  2776.      * Attention: Some platforms only support currentDatabase when they
  2777.      * are connected with that database. Cross-database information schema
  2778.      * requests may be impossible.
  2779.      *
  2780.      * @param string $table
  2781.      * @param string $database
  2782.      *
  2783.      * @return string
  2784.      *
  2785.      * @throws Exception If not supported on this platform.
  2786.      */
  2787.     public function getListTableIndexesSQL($table$database null)
  2788.     {
  2789.         throw Exception::notSupported(__METHOD__);
  2790.     }
  2791.     /**
  2792.      * @param string $table
  2793.      *
  2794.      * @return string
  2795.      *
  2796.      * @throws Exception If not supported on this platform.
  2797.      */
  2798.     public function getListTableForeignKeysSQL($table)
  2799.     {
  2800.         throw Exception::notSupported(__METHOD__);
  2801.     }
  2802.     /**
  2803.      * @param string $name
  2804.      * @param string $sql
  2805.      *
  2806.      * @return string
  2807.      */
  2808.     public function getCreateViewSQL($name$sql)
  2809.     {
  2810.         return 'CREATE VIEW ' $name ' AS ' $sql;
  2811.     }
  2812.     /**
  2813.      * @param string $name
  2814.      *
  2815.      * @return string
  2816.      */
  2817.     public function getDropViewSQL($name)
  2818.     {
  2819.         return 'DROP VIEW ' $name;
  2820.     }
  2821.     /**
  2822.      * @param string $sequence
  2823.      *
  2824.      * @return string
  2825.      *
  2826.      * @throws Exception If not supported on this platform.
  2827.      */
  2828.     public function getSequenceNextValSQL($sequence)
  2829.     {
  2830.         throw Exception::notSupported(__METHOD__);
  2831.     }
  2832.     /**
  2833.      * Returns the SQL to create a new database.
  2834.      *
  2835.      * @param string $name The name of the database that should be created.
  2836.      *
  2837.      * @return string
  2838.      *
  2839.      * @throws Exception If not supported on this platform.
  2840.      */
  2841.     public function getCreateDatabaseSQL($name)
  2842.     {
  2843.         if (! $this->supportsCreateDropDatabase()) {
  2844.             throw Exception::notSupported(__METHOD__);
  2845.         }
  2846.         return 'CREATE DATABASE ' $name;
  2847.     }
  2848.     /**
  2849.      * Returns the SQL snippet to drop an existing database.
  2850.      *
  2851.      * @param string $name The name of the database that should be dropped.
  2852.      *
  2853.      * @return string
  2854.      */
  2855.     public function getDropDatabaseSQL($name)
  2856.     {
  2857.         if (! $this->supportsCreateDropDatabase()) {
  2858.             throw Exception::notSupported(__METHOD__);
  2859.         }
  2860.         return 'DROP DATABASE ' $name;
  2861.     }
  2862.     /**
  2863.      * Returns the SQL to set the transaction isolation level.
  2864.      *
  2865.      * @param int $level
  2866.      *
  2867.      * @return string
  2868.      *
  2869.      * @throws Exception If not supported on this platform.
  2870.      */
  2871.     public function getSetTransactionIsolationSQL($level)
  2872.     {
  2873.         throw Exception::notSupported(__METHOD__);
  2874.     }
  2875.     /**
  2876.      * Obtains DBMS specific SQL to be used to create datetime columns in
  2877.      * statements like CREATE TABLE.
  2878.      *
  2879.      * @param mixed[] $column
  2880.      *
  2881.      * @return string
  2882.      *
  2883.      * @throws Exception If not supported on this platform.
  2884.      */
  2885.     public function getDateTimeTypeDeclarationSQL(array $column)
  2886.     {
  2887.         throw Exception::notSupported(__METHOD__);
  2888.     }
  2889.     /**
  2890.      * Obtains DBMS specific SQL to be used to create datetime with timezone offset columns.
  2891.      *
  2892.      * @param mixed[] $column
  2893.      *
  2894.      * @return string
  2895.      */
  2896.     public function getDateTimeTzTypeDeclarationSQL(array $column)
  2897.     {
  2898.         return $this->getDateTimeTypeDeclarationSQL($column);
  2899.     }
  2900.     /**
  2901.      * Obtains DBMS specific SQL to be used to create date columns in statements
  2902.      * like CREATE TABLE.
  2903.      *
  2904.      * @param mixed[] $column
  2905.      *
  2906.      * @return string
  2907.      *
  2908.      * @throws Exception If not supported on this platform.
  2909.      */
  2910.     public function getDateTypeDeclarationSQL(array $column)
  2911.     {
  2912.         throw Exception::notSupported(__METHOD__);
  2913.     }
  2914.     /**
  2915.      * Obtains DBMS specific SQL to be used to create time columns in statements
  2916.      * like CREATE TABLE.
  2917.      *
  2918.      * @param mixed[] $column
  2919.      *
  2920.      * @return string
  2921.      *
  2922.      * @throws Exception If not supported on this platform.
  2923.      */
  2924.     public function getTimeTypeDeclarationSQL(array $column)
  2925.     {
  2926.         throw Exception::notSupported(__METHOD__);
  2927.     }
  2928.     /**
  2929.      * @param mixed[] $column
  2930.      *
  2931.      * @return string
  2932.      */
  2933.     public function getFloatDeclarationSQL(array $column)
  2934.     {
  2935.         return 'DOUBLE PRECISION';
  2936.     }
  2937.     /**
  2938.      * Gets the default transaction isolation level of the platform.
  2939.      *
  2940.      * @see TransactionIsolationLevel
  2941.      *
  2942.      * @return int The default isolation level.
  2943.      */
  2944.     public function getDefaultTransactionIsolationLevel()
  2945.     {
  2946.         return TransactionIsolationLevel::READ_COMMITTED;
  2947.     }
  2948.     /* supports*() methods */
  2949.     /**
  2950.      * Whether the platform supports sequences.
  2951.      *
  2952.      * @return bool
  2953.      */
  2954.     public function supportsSequences()
  2955.     {
  2956.         return false;
  2957.     }
  2958.     /**
  2959.      * Whether the platform supports identity columns.
  2960.      *
  2961.      * Identity columns are columns that receive an auto-generated value from the
  2962.      * database on insert of a row.
  2963.      *
  2964.      * @return bool
  2965.      */
  2966.     public function supportsIdentityColumns()
  2967.     {
  2968.         return false;
  2969.     }
  2970.     /**
  2971.      * Whether the platform emulates identity columns through sequences.
  2972.      *
  2973.      * Some platforms that do not support identity columns natively
  2974.      * but support sequences can emulate identity columns by using
  2975.      * sequences.
  2976.      *
  2977.      * @return bool
  2978.      */
  2979.     public function usesSequenceEmulatedIdentityColumns()
  2980.     {
  2981.         return false;
  2982.     }
  2983.     /**
  2984.      * Returns the name of the sequence for a particular identity column in a particular table.
  2985.      *
  2986.      * @see usesSequenceEmulatedIdentityColumns
  2987.      *
  2988.      * @param string $tableName  The name of the table to return the sequence name for.
  2989.      * @param string $columnName The name of the identity column in the table to return the sequence name for.
  2990.      *
  2991.      * @return string
  2992.      *
  2993.      * @throws Exception If not supported on this platform.
  2994.      */
  2995.     public function getIdentitySequenceName($tableName$columnName)
  2996.     {
  2997.         throw Exception::notSupported(__METHOD__);
  2998.     }
  2999.     /**
  3000.      * Whether the platform supports indexes.
  3001.      *
  3002.      * @deprecated
  3003.      *
  3004.      * @return bool
  3005.      */
  3006.     public function supportsIndexes()
  3007.     {
  3008.         Deprecation::trigger(
  3009.             'doctrine/dbal',
  3010.             'https://github.com/doctrine/dbal/pulls/4724',
  3011.             'AbstractPlatform::supportsIndexes() is deprecated.'
  3012.         );
  3013.         return true;
  3014.     }
  3015.     /**
  3016.      * Whether the platform supports partial indexes.
  3017.      *
  3018.      * @return bool
  3019.      */
  3020.     public function supportsPartialIndexes()
  3021.     {
  3022.         return false;
  3023.     }
  3024.     /**
  3025.      * Whether the platform supports indexes with column length definitions.
  3026.      */
  3027.     public function supportsColumnLengthIndexes(): bool
  3028.     {
  3029.         return false;
  3030.     }
  3031.     /**
  3032.      * Whether the platform supports altering tables.
  3033.      *
  3034.      * @deprecated All platforms must implement altering tables.
  3035.      *
  3036.      * @return bool
  3037.      */
  3038.     public function supportsAlterTable()
  3039.     {
  3040.         Deprecation::trigger(
  3041.             'doctrine/dbal',
  3042.             'https://github.com/doctrine/dbal/pulls/4724',
  3043.             'AbstractPlatform::supportsAlterTable() is deprecated. All platforms must implement altering tables.'
  3044.         );
  3045.         return true;
  3046.     }
  3047.     /**
  3048.      * Whether the platform supports transactions.
  3049.      *
  3050.      * @deprecated
  3051.      *
  3052.      * @return bool
  3053.      */
  3054.     public function supportsTransactions()
  3055.     {
  3056.         Deprecation::trigger(
  3057.             'doctrine/dbal',
  3058.             'https://github.com/doctrine/dbal/pulls/4724',
  3059.             'AbstractPlatform::supportsTransactions() is deprecated.'
  3060.         );
  3061.         return true;
  3062.     }
  3063.     /**
  3064.      * Whether the platform supports savepoints.
  3065.      *
  3066.      * @return bool
  3067.      */
  3068.     public function supportsSavepoints()
  3069.     {
  3070.         return true;
  3071.     }
  3072.     /**
  3073.      * Whether the platform supports releasing savepoints.
  3074.      *
  3075.      * @return bool
  3076.      */
  3077.     public function supportsReleaseSavepoints()
  3078.     {
  3079.         return $this->supportsSavepoints();
  3080.     }
  3081.     /**
  3082.      * Whether the platform supports primary key constraints.
  3083.      *
  3084.      * @deprecated
  3085.      *
  3086.      * @return bool
  3087.      */
  3088.     public function supportsPrimaryConstraints()
  3089.     {
  3090.         Deprecation::trigger(
  3091.             'doctrine/dbal',
  3092.             'https://github.com/doctrine/dbal/pulls/4724',
  3093.             'AbstractPlatform::supportsPrimaryConstraints() is deprecated.'
  3094.         );
  3095.         return true;
  3096.     }
  3097.     /**
  3098.      * Whether the platform supports foreign key constraints.
  3099.      *
  3100.      * @return bool
  3101.      */
  3102.     public function supportsForeignKeyConstraints()
  3103.     {
  3104.         return true;
  3105.     }
  3106.     /**
  3107.      * Whether the platform supports database schemas.
  3108.      *
  3109.      * @return bool
  3110.      */
  3111.     public function supportsSchemas()
  3112.     {
  3113.         return false;
  3114.     }
  3115.     /**
  3116.      * Whether this platform can emulate schemas.
  3117.      *
  3118.      * @deprecated
  3119.      *
  3120.      * Platforms that either support or emulate schemas don't automatically
  3121.      * filter a schema for the namespaced elements in {@see AbstractManager::createSchema()}.
  3122.      *
  3123.      * @return bool
  3124.      */
  3125.     public function canEmulateSchemas()
  3126.     {
  3127.         Deprecation::trigger(
  3128.             'doctrine/dbal',
  3129.             'https://github.com/doctrine/dbal/pull/4805',
  3130.             'AbstractPlatform::canEmulateSchemas() is deprecated.'
  3131.         );
  3132.         return false;
  3133.     }
  3134.     /**
  3135.      * Returns the default schema name.
  3136.      *
  3137.      * @return string
  3138.      *
  3139.      * @throws Exception If not supported on this platform.
  3140.      */
  3141.     public function getDefaultSchemaName()
  3142.     {
  3143.         throw Exception::notSupported(__METHOD__);
  3144.     }
  3145.     /**
  3146.      * Whether this platform supports create database.
  3147.      *
  3148.      * Some databases don't allow to create and drop databases at all or only with certain tools.
  3149.      *
  3150.      * @return bool
  3151.      */
  3152.     public function supportsCreateDropDatabase()
  3153.     {
  3154.         return true;
  3155.     }
  3156.     /**
  3157.      * Whether the platform supports getting the affected rows of a recent update/delete type query.
  3158.      *
  3159.      * @deprecated
  3160.      *
  3161.      * @return bool
  3162.      */
  3163.     public function supportsGettingAffectedRows()
  3164.     {
  3165.         Deprecation::trigger(
  3166.             'doctrine/dbal',
  3167.             'https://github.com/doctrine/dbal/pulls/4724',
  3168.             'AbstractPlatform::supportsGettingAffectedRows() is deprecated.'
  3169.         );
  3170.         return true;
  3171.     }
  3172.     /**
  3173.      * Whether this platform support to add inline column comments as postfix.
  3174.      *
  3175.      * @return bool
  3176.      */
  3177.     public function supportsInlineColumnComments()
  3178.     {
  3179.         return false;
  3180.     }
  3181.     /**
  3182.      * Whether this platform support the proprietary syntax "COMMENT ON asset".
  3183.      *
  3184.      * @return bool
  3185.      */
  3186.     public function supportsCommentOnStatement()
  3187.     {
  3188.         return false;
  3189.     }
  3190.     /**
  3191.      * Does this platform have native guid type.
  3192.      *
  3193.      * @return bool
  3194.      */
  3195.     public function hasNativeGuidType()
  3196.     {
  3197.         return false;
  3198.     }
  3199.     /**
  3200.      * Does this platform have native JSON type.
  3201.      *
  3202.      * @return bool
  3203.      */
  3204.     public function hasNativeJsonType()
  3205.     {
  3206.         return false;
  3207.     }
  3208.     /**
  3209.      * Whether this platform supports views.
  3210.      *
  3211.      * @deprecated All platforms must implement support for views.
  3212.      *
  3213.      * @return bool
  3214.      */
  3215.     public function supportsViews()
  3216.     {
  3217.         Deprecation::trigger(
  3218.             'doctrine/dbal',
  3219.             'https://github.com/doctrine/dbal/pulls/4724',
  3220.             'AbstractPlatform::supportsViews() is deprecated. All platforms must implement support for views.'
  3221.         );
  3222.         return true;
  3223.     }
  3224.     /**
  3225.      * Does this platform support column collation?
  3226.      *
  3227.      * @return bool
  3228.      */
  3229.     public function supportsColumnCollation()
  3230.     {
  3231.         return false;
  3232.     }
  3233.     /**
  3234.      * Gets the format string, as accepted by the date() function, that describes
  3235.      * the format of a stored datetime value of this platform.
  3236.      *
  3237.      * @return string The format string.
  3238.      */
  3239.     public function getDateTimeFormatString()
  3240.     {
  3241.         return 'Y-m-d H:i:s';
  3242.     }
  3243.     /**
  3244.      * Gets the format string, as accepted by the date() function, that describes
  3245.      * the format of a stored datetime with timezone value of this platform.
  3246.      *
  3247.      * @return string The format string.
  3248.      */
  3249.     public function getDateTimeTzFormatString()
  3250.     {
  3251.         return 'Y-m-d H:i:s';
  3252.     }
  3253.     /**
  3254.      * Gets the format string, as accepted by the date() function, that describes
  3255.      * the format of a stored date value of this platform.
  3256.      *
  3257.      * @return string The format string.
  3258.      */
  3259.     public function getDateFormatString()
  3260.     {
  3261.         return 'Y-m-d';
  3262.     }
  3263.     /**
  3264.      * Gets the format string, as accepted by the date() function, that describes
  3265.      * the format of a stored time value of this platform.
  3266.      *
  3267.      * @return string The format string.
  3268.      */
  3269.     public function getTimeFormatString()
  3270.     {
  3271.         return 'H:i:s';
  3272.     }
  3273.     /**
  3274.      * Adds an driver-specific LIMIT clause to the query.
  3275.      *
  3276.      * @param string   $query
  3277.      * @param int|null $limit
  3278.      * @param int      $offset
  3279.      *
  3280.      * @throws Exception
  3281.      */
  3282.     final public function modifyLimitQuery($query$limit$offset 0): string
  3283.     {
  3284.         if ($offset 0) {
  3285.             throw new Exception(sprintf(
  3286.                 'Offset must be a positive integer or zero, %d given',
  3287.                 $offset
  3288.             ));
  3289.         }
  3290.         if ($offset && ! $this->supportsLimitOffset()) {
  3291.             throw new Exception(sprintf(
  3292.                 'Platform %s does not support offset values in limit queries.',
  3293.                 $this->getName()
  3294.             ));
  3295.         }
  3296.         if ($limit !== null) {
  3297.             $limit = (int) $limit;
  3298.         }
  3299.         return $this->doModifyLimitQuery($query$limit, (int) $offset);
  3300.     }
  3301.     /**
  3302.      * Adds an platform-specific LIMIT clause to the query.
  3303.      *
  3304.      * @param string   $query
  3305.      * @param int|null $limit
  3306.      * @param int      $offset
  3307.      *
  3308.      * @return string
  3309.      */
  3310.     protected function doModifyLimitQuery($query$limit$offset)
  3311.     {
  3312.         if ($limit !== null) {
  3313.             $query .= sprintf(' LIMIT %d'$limit);
  3314.         }
  3315.         if ($offset 0) {
  3316.             $query .= sprintf(' OFFSET %d'$offset);
  3317.         }
  3318.         return $query;
  3319.     }
  3320.     /**
  3321.      * Whether the database platform support offsets in modify limit clauses.
  3322.      *
  3323.      * @deprecated All platforms must implement support for offsets in modify limit clauses.
  3324.      *
  3325.      * @return bool
  3326.      */
  3327.     public function supportsLimitOffset()
  3328.     {
  3329.         Deprecation::triggerIfCalledFromOutside(
  3330.             'doctrine/dbal',
  3331.             'https://github.com/doctrine/dbal/pulls/4724',
  3332.             'AbstractPlatform::supportsViews() is deprecated.'
  3333.             ' All platforms must implement support for offsets in modify limit clauses.'
  3334.         );
  3335.         return true;
  3336.     }
  3337.     /**
  3338.      * Maximum length of any given database identifier, like tables or column names.
  3339.      *
  3340.      * @return int
  3341.      */
  3342.     public function getMaxIdentifierLength()
  3343.     {
  3344.         return 63;
  3345.     }
  3346.     /**
  3347.      * Returns the insert SQL for an empty insert statement.
  3348.      *
  3349.      * @param string $quotedTableName
  3350.      * @param string $quotedIdentifierColumnName
  3351.      *
  3352.      * @return string
  3353.      */
  3354.     public function getEmptyIdentityInsertSQL($quotedTableName$quotedIdentifierColumnName)
  3355.     {
  3356.         return 'INSERT INTO ' $quotedTableName ' (' $quotedIdentifierColumnName ') VALUES (null)';
  3357.     }
  3358.     /**
  3359.      * Generates a Truncate Table SQL statement for a given table.
  3360.      *
  3361.      * Cascade is not supported on many platforms but would optionally cascade the truncate by
  3362.      * following the foreign keys.
  3363.      *
  3364.      * @param string $tableName
  3365.      * @param bool   $cascade
  3366.      *
  3367.      * @return string
  3368.      */
  3369.     public function getTruncateTableSQL($tableName$cascade false)
  3370.     {
  3371.         $tableIdentifier = new Identifier($tableName);
  3372.         return 'TRUNCATE ' $tableIdentifier->getQuotedName($this);
  3373.     }
  3374.     /**
  3375.      * This is for test reasons, many vendors have special requirements for dummy statements.
  3376.      *
  3377.      * @return string
  3378.      */
  3379.     public function getDummySelectSQL()
  3380.     {
  3381.         $expression func_num_args() > func_get_arg(0) : '1';
  3382.         return sprintf('SELECT %s'$expression);
  3383.     }
  3384.     /**
  3385.      * Returns the SQL to create a new savepoint.
  3386.      *
  3387.      * @param string $savepoint
  3388.      *
  3389.      * @return string
  3390.      */
  3391.     public function createSavePoint($savepoint)
  3392.     {
  3393.         return 'SAVEPOINT ' $savepoint;
  3394.     }
  3395.     /**
  3396.      * Returns the SQL to release a savepoint.
  3397.      *
  3398.      * @param string $savepoint
  3399.      *
  3400.      * @return string
  3401.      */
  3402.     public function releaseSavePoint($savepoint)
  3403.     {
  3404.         return 'RELEASE SAVEPOINT ' $savepoint;
  3405.     }
  3406.     /**
  3407.      * Returns the SQL to rollback a savepoint.
  3408.      *
  3409.      * @param string $savepoint
  3410.      *
  3411.      * @return string
  3412.      */
  3413.     public function rollbackSavePoint($savepoint)
  3414.     {
  3415.         return 'ROLLBACK TO SAVEPOINT ' $savepoint;
  3416.     }
  3417.     /**
  3418.      * Returns the keyword list instance of this platform.
  3419.      *
  3420.      * @throws Exception If no keyword list is specified.
  3421.      */
  3422.     final public function getReservedKeywordsList(): KeywordList
  3423.     {
  3424.         // Check for an existing instantiation of the keywords class.
  3425.         if ($this->_keywords === null) {
  3426.             // Store the instance so it doesn't need to be generated on every request.
  3427.             $this->_keywords $this->createReservedKeywordsList();
  3428.         }
  3429.         return $this->_keywords;
  3430.     }
  3431.     /**
  3432.      * Creates an instance of the reserved keyword list of this platform.
  3433.      *
  3434.      * This method will become @abstract in DBAL 4.0.0.
  3435.      *
  3436.      * @throws Exception
  3437.      */
  3438.     protected function createReservedKeywordsList(): KeywordList
  3439.     {
  3440.         $class    $this->getReservedKeywordsClass();
  3441.         $keywords = new $class();
  3442.         if (! $keywords instanceof KeywordList) {
  3443.             throw Exception::notSupported(__METHOD__);
  3444.         }
  3445.         return $keywords;
  3446.     }
  3447.     /**
  3448.      * Returns the class name of the reserved keywords list.
  3449.      *
  3450.      * @deprecated Implement {@see createReservedKeywordsList()} instead.
  3451.      *
  3452.      * @return string
  3453.      * @psalm-return class-string<KeywordList>
  3454.      *
  3455.      * @throws Exception If not supported on this platform.
  3456.      */
  3457.     protected function getReservedKeywordsClass()
  3458.     {
  3459.         Deprecation::triggerIfCalledFromOutside(
  3460.             'doctrine/dbal',
  3461.             'https://github.com/doctrine/dbal/issues/4510',
  3462.             'AbstractPlatform::getReservedKeywordsClass() is deprecated,'
  3463.                 ' use AbstractPlatform::createReservedKeywordsList() instead.'
  3464.         );
  3465.         throw Exception::notSupported(__METHOD__);
  3466.     }
  3467.     /**
  3468.      * Quotes a literal string.
  3469.      * This method is NOT meant to fix SQL injections!
  3470.      * It is only meant to escape this platform's string literal
  3471.      * quote character inside the given literal string.
  3472.      *
  3473.      * @param string $str The literal string to be quoted.
  3474.      *
  3475.      * @return string The quoted literal string.
  3476.      */
  3477.     public function quoteStringLiteral($str)
  3478.     {
  3479.         $c $this->getStringLiteralQuoteCharacter();
  3480.         return $c str_replace($c$c $c$str) . $c;
  3481.     }
  3482.     /**
  3483.      * Gets the character used for string literal quoting.
  3484.      *
  3485.      * @return string
  3486.      */
  3487.     public function getStringLiteralQuoteCharacter()
  3488.     {
  3489.         return "'";
  3490.     }
  3491.     /**
  3492.      * Escapes metacharacters in a string intended to be used with a LIKE
  3493.      * operator.
  3494.      *
  3495.      * @param string $inputString a literal, unquoted string
  3496.      * @param string $escapeChar  should be reused by the caller in the LIKE
  3497.      *                            expression.
  3498.      */
  3499.     final public function escapeStringForLike(string $inputStringstring $escapeChar): string
  3500.     {
  3501.         return preg_replace(
  3502.             '~([' preg_quote($this->getLikeWildcardCharacters() . $escapeChar'~') . '])~u',
  3503.             addcslashes($escapeChar'\\') . '$1',
  3504.             $inputString
  3505.         );
  3506.     }
  3507.     /**
  3508.      * @return array<string,mixed> An associative array with the name of the properties
  3509.      *                             of the column being declared as array indexes.
  3510.      */
  3511.     private function columnToArray(Column $column): array
  3512.     {
  3513.         $name $column->getQuotedName($this);
  3514.         $columnData array_merge($column->toArray(), [
  3515.             'name' => $name,
  3516.             'version' => $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false,
  3517.             'comment' => $this->getColumnComment($column),
  3518.         ]);
  3519.         if ($columnData['type'] instanceof Types\StringType && $columnData['length'] === null) {
  3520.             $columnData['length'] = $this->getVarcharDefaultLength();
  3521.         }
  3522.         return $columnData;
  3523.     }
  3524.     /**
  3525.      * @internal
  3526.      */
  3527.     public function createSQLParser(): Parser
  3528.     {
  3529.         return new Parser(false);
  3530.     }
  3531.     protected function getLikeWildcardCharacters(): string
  3532.     {
  3533.         return '%_';
  3534.     }
  3535.     /**
  3536.      * Compares the definitions of the given columns in the context of this platform.
  3537.      *
  3538.      * @throws Exception
  3539.      */
  3540.     public function columnsEqual(Column $column1Column $column2): bool
  3541.     {
  3542.         $column1Array $this->columnToArray($column1);
  3543.         $column2Array $this->columnToArray($column2);
  3544.         // ignore explicit columnDefinition since it's not set on the Column generated by the SchemaManager
  3545.         unset($column1Array['columnDefinition']);
  3546.         unset($column2Array['columnDefinition']);
  3547.         if (
  3548.             $this->getColumnDeclarationSQL(''$column1Array)
  3549.             !== $this->getColumnDeclarationSQL(''$column2Array)
  3550.         ) {
  3551.             return false;
  3552.         }
  3553.         // If the platform supports inline comments, all comparison is already done above
  3554.         if ($this->supportsInlineColumnComments()) {
  3555.             return true;
  3556.         }
  3557.         if ($column1->getComment() !== $column2->getComment()) {
  3558.             return false;
  3559.         }
  3560.         return $column1->getType() === $column2->getType();
  3561.     }
  3562. }