vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php line 397

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