diff --git a/src/db/ColumnSchemaBuilder.php b/src/db/ColumnSchemaBuilder.php index d041d457..bda85a9d 100644 --- a/src/db/ColumnSchemaBuilder.php +++ b/src/db/ColumnSchemaBuilder.php @@ -1,9 +1,6 @@ - * @since 2.0.6 */ -class ColumnSchemaBuilder extends BaseObject +class ColumnSchemaBuilder extends BaseObject implements \Stringable { - // Internally used constants representing categories that abstract column types fall under. - // See [[$categoryMap]] for mappings of abstract column types to category. - // @since 2.0.8 - const CATEGORY_PK = 'pk'; - const CATEGORY_STRING = 'string'; - const CATEGORY_NUMERIC = 'numeric'; - const CATEGORY_TIME = 'time'; - const CATEGORY_OTHER = 'other'; + /** + * Abstract column type for `INTEGER AUTO_INCREMENT`. + */ + public const CATEGORY_AUTO = 'auto'; /** - * @var string the column type definition such as INTEGER, VARCHAR, DATETIME, etc. + * Abstract column type for `BIGINT AUTO_INCREMENT`. */ - protected $type; + public const CATEGORY_BIGAUTO = 'bigauto'; + /** - * @var int|string|array column size or precision definition. This is what goes into the parenthesis after - * the column type. This can be either a string, an integer or an array. If it is an array, the array values will - * be joined into a string separated by comma. + * Abstract column type for `INTEGER AUTO_INCREMENT PRIMARY KEY`. */ - protected $length; + public const CATEGORY_PK = 'pk'; + /** - * @var bool|null whether the column is or not nullable. If this is `true`, a `NOT NULL` constraint will be added. - * If this is `false`, a `NULL` constraint will be added. + * Abstract column type for `BIGINT AUTO_INCREMENT PRIMARY KEY`. */ - protected $isNotNull; + public const CATEGORY_BIGPK = 'bigpk'; + /** - * @var bool whether the column values should be unique. If this is `true`, a `UNIQUE` constraint will be added. + * Abstract column type for `STRING` columns. */ - protected $isUnique = false; + public const CATEGORY_STRING = 'string'; + /** - * @var string the `CHECK` constraint for the column. + * Abstract column type for `NUMERIC` columns. */ - protected $check; + public const CATEGORY_NUMERIC = 'numeric'; + /** - * @var mixed default value of the column. + * Abstract column type for `TIME` columns. */ - protected $default; + public const CATEGORY_TIME = 'time'; + + /** + * Abstract column type for `OTHER` columns. + */ + public const CATEGORY_OTHER = 'other'; + + /** + * The column types that are auto-incremental or primary key. + */ + public const CATEGORY_GROUP_AUTO_PK = [ + self::CATEGORY_AUTO, + self::CATEGORY_BIGAUTO, + self::CATEGORY_PK, + self::CATEGORY_BIGPK, + ]; + /** * @var mixed SQL string to be appended to column schema definition. - * @since 2.0.9 */ - protected $append; + protected mixed $append = null; + /** - * @var bool whether the column values should be unsigned. If this is `true`, an `UNSIGNED` keyword will be added. - * @since 2.0.7 + * @var string|null the `CHECK` constraint for the column. */ - protected $isUnsigned = false; + protected string|null $check = null; + /** - * @var string the column after which this column will be added. - * @since 2.0.8 + * @var Connection the database connection. This is used mainly to escape table and column names. */ - protected $after; + protected Connection $db; + /** - * @var bool whether this column is to be inserted at the beginning of the table. - * @since 2.0.8 + * @var mixed default value of the column. */ - protected $isFirst; + protected mixed $default = null; + /** + * @var bool|null whether the column is or not nullable. If this is `true`, a `NOT NULL` constraint will be added. + * If this is `false`, a `NULL` constraint will be added. + */ + protected bool|null $isNotNull = null; + + /** + * @var bool whether the column values should be unique. If this is `true`, a `UNIQUE` constraint will be added. + */ + protected bool $isUnique = false; + + /** + * @var array|int|string|null column size or precision definition. This is what goes into the parenthesis after + * the column type. This can be either a string, an integer or an array. If it is an array, the array values will + * be joined into a string separated by comma. + */ + protected array|int|string|null $length = null; + + /** + * @var string|null the column type definition such as INTEGER, VARCHAR, DATETIME, etc. + */ + protected string|null $type = null; + + /** + * @var string|null comment value of the column. + */ + public string|null $comment = null; /** * @var array mapping of abstract column types (keys) to type categories (values). - * @since 2.0.43 */ - public static $typeCategoryMap = [ + public static array $typeCategoryMap = [ + // auto-incremental column types + Schema::TYPE_AUTO => self::CATEGORY_AUTO, + Schema::TYPE_BIGAUTO => self::CATEGORY_BIGAUTO, + + // primary key column types Schema::TYPE_PK => self::CATEGORY_PK, - Schema::TYPE_UPK => self::CATEGORY_PK, - Schema::TYPE_BIGPK => self::CATEGORY_PK, - Schema::TYPE_UBIGPK => self::CATEGORY_PK, + Schema::TYPE_BIGPK => self::CATEGORY_BIGPK, + Schema::TYPE_CHAR => self::CATEGORY_STRING, Schema::TYPE_STRING => self::CATEGORY_STRING, Schema::TYPE_TEXT => self::CATEGORY_STRING, @@ -107,284 +144,245 @@ class ColumnSchemaBuilder extends BaseObject Schema::TYPE_BOOLEAN => self::CATEGORY_NUMERIC, Schema::TYPE_MONEY => self::CATEGORY_NUMERIC, ]; - /** - * @var \yii\db\Connection the current database connection. It is used mainly to escape strings - * safely when building the final column schema string. - * @since 2.0.8 - */ - public $db; - /** - * @var string comment value of the column. - * @since 2.0.8 - */ - public $comment; /** * Create a column schema builder instance giving the type and value precision. * * @param string $type type of the column. See [[$type]]. - * @param int|string|array|null $length length or precision of the column. See [[$length]]. - * @param \yii\db\Connection|null $db the current database connection. See [[$db]]. - * @param array $config name-value pairs that will be used to initialize the object properties - */ - public function __construct($type, $length = null, $db = null, $config = []) - { + * @param array|int|string|null $length length or precision of the column. See [[$length]]. + * @param Connection|null $db the current database connection. See [[$db]]. + * @param array $config name-value pairs that will be used to initialize the object properties. + */ + public function __construct( + Connection $db, + string|null $type = null, + array|int|string|null $length = null, + array $config = [] + ) { + $this->db = $db; $this->type = $type; $this->length = $length; - $this->db = $db; + parent::__construct($config); } /** - * Adds a `NOT NULL` constraint to the column. - * @return $this + * Builds the full string for the column's schema. + * + * @return string */ - public function notNull() + public function __toString(): string { - $this->isNotNull = true; - return $this; - } + $format = match ($this->getTypeCategory()) { + self::CATEGORY_PK => '{type}{check}{comment}{append}', + default => '{type}{length}{notnull}{unique}{default}{check}{comment}{append}', + }; - /** - * Adds a `NULL` constraint to the column. - * @return $this - * @since 2.0.9 - */ - public function null() - { - $this->isNotNull = false; - return $this; + return $this->buildCompleteString($format); } /** - * Adds a `UNIQUE` constraint to the column. - * @return $this + * Specify additional SQL to be appended to column definition. + * Position modifiers will be appended after column definition in databases that support them. + * + * @param string $sql the SQL string to be appended. + * + * @return static Instance of the column schema builder. */ - public function unique() + public function append(string $sql): static { - $this->isUnique = true; + $this->append = $sql; + return $this; } /** * Sets a `CHECK` constraint for the column. + * * @param string $check the SQL of the `CHECK` constraint to be added. - * @return $this + * + * @return static Instance of the column schema builder. */ - public function check($check) + public function check(string $check): static { $this->check = $check; - return $this; - } - /** - * Specify the default value for the column. - * @param mixed $default the default value. - * @return $this - */ - public function defaultValue($default) - { - if ($default === null) { - $this->null(); - } - - $this->default = $default; return $this; } /** * Specifies the comment for column. - * @param string $comment the comment - * @return $this - * @since 2.0.8 + * + * @param string $comment the comment. + * + * @return static Instance of the column schema builder. */ - public function comment($comment) + public function comment(string $comment): static { $this->comment = $comment; + return $this; } /** - * Marks column as unsigned. - * @return $this - * @since 2.0.7 + * Specify the default SQL expression for the column. + * + * @param string $default the default value expression. + * + * @return static Instance of the column schema builder. */ - public function unsigned() + public function defaultExpression(string $default): static { - switch ($this->type) { - case Schema::TYPE_PK: - $this->type = Schema::TYPE_UPK; - break; - case Schema::TYPE_BIGPK: - $this->type = Schema::TYPE_UBIGPK; - break; - } - $this->isUnsigned = true; + $this->default = new Expression($default); + return $this; } /** - * Adds an `AFTER` constraint to the column. - * Note: MySQL and Oracle support only. - * @param string $after the column after which $this column will be added. - * @return $this - * @since 2.0.8 + * Specify the default value for the column. + * + * @param mixed $default the default value. + * + * @return static Instance of the column schema builder. */ - public function after($after) + public function defaultValue(mixed $default): static { - $this->after = $after; + if ($default === null) { + $this->null(); + } + + $this->default = $default; + return $this; } /** - * Adds an `FIRST` constraint to the column. - * Note: MySQL and Oracle support only. - * @return $this - * @since 2.0.8 + * @return array mapping of abstract column types (keys) to type categories (values). */ - public function first() + public function getCategoryMap(): array { - $this->isFirst = true; - return $this; + return static::$typeCategoryMap; } /** - * Specify the default SQL expression for the column. - * @param string $default the default value expression. - * @return $this - * @since 2.0.7 + * Returns the column type. + * + * @return string|null the column type. */ - public function defaultExpression($default) + public function getType(): string|null { - $this->default = new Expression($default); - return $this; + return $this->type; } /** - * Specify additional SQL to be appended to column definition. - * Position modifiers will be appended after column definition in databases that support them. - * @param string $sql the SQL string to be appended. - * @return $this - * @since 2.0.9 + * Adds a `NOT NULL` constraint to the column. + * + * @return static Instance of the column schema builder. */ - public function append($sql) + public function notNull(): static { - $this->append = $sql; + $this->isNotNull = true; + return $this; } /** - * Builds the full string for the column's schema. - * @return string + * Adds a `NULL` constraint to the column. + * + * @return static Instance of the column schema builder. */ - public function __toString() + public function null(): static { - switch ($this->getTypeCategory()) { - case self::CATEGORY_PK: - $format = '{type}{check}{comment}{append}'; - break; - default: - $format = '{type}{length}{notnull}{unique}{default}{check}{comment}{append}'; - } + $this->isNotNull = false; - return $this->buildCompleteString($format); + return $this; } /** - * @return array mapping of abstract column types (keys) to type categories (values). - * @since 2.0.43 + * @param array $categoryMap mapping of abstract column types (keys) to type categories (values). */ - public function getCategoryMap() + public function setCategoryMap(array $categoryMap): void { - return static::$typeCategoryMap; + static::$typeCategoryMap = $categoryMap; } /** - * @param array $categoryMap mapping of abstract column types (keys) to type categories (values). - * @since 2.0.43 + * Adds a `UNIQUE` constraint to the column. + * + * @return static Instance of the column schema builder. */ - public function setCategoryMap($categoryMap) + public function unique(): static { - static::$typeCategoryMap = $categoryMap; + $this->isUnique = true; + + return $this; } /** - * Builds the length/precision part of the column. - * @return string + * Builds the custom string that's appended to column definition. + * + * @return string custom string to append. */ - protected function buildLengthString() + protected function buildAppendString(): string { - if ($this->length === null || $this->length === []) { - return ''; - } - if (is_array($this->length)) { - $this->length = implode(',', $this->length); - } - - return "({$this->length})"; + return $this->append !== null ? ' ' . $this->append : ''; } /** - * Builds the not null constraint for the column. - * @return string returns 'NOT NULL' if [[isNotNull]] is true, - * 'NULL' if [[isNotNull]] is false or an empty string otherwise. + * Builds the check constraint for the column. + * + * @return string a string containing the CHECK constraint. */ - protected function buildNotNullString() + protected function buildCheckString(): string { - if ($this->isNotNull === true) { - return ' NOT NULL'; - } elseif ($this->isNotNull === false) { - return ' NULL'; - } - - return ''; + return $this->check !== null ? " CHECK ({$this->check})" : ''; } /** - * Builds the unique constraint for the column. - * @return string returns string 'UNIQUE' if [[isUnique]] is true, otherwise it returns an empty string. + * Builds the comment specification for the column. + * + * @return string a string containing the COMMENT keyword and the comment itself. */ - protected function buildUniqueString() + protected function buildCommentString(): string { - return $this->isUnique ? ' UNIQUE' : ''; + return ''; } /** - * Return the default value for the column. - * @return string|null string with default value of column. + * Returns the complete column definition from input format. + * + * @param string $format the format of the definition. + * @param array $customPlaceHolder custom placeholder values. + * + * @return string a string containing the complete column definition. */ - protected function buildDefaultValue() + protected function buildCompleteString(string $format, array $customPlaceHolder = []): string { - if ($this->default === null) { - return $this->isNotNull === false ? 'NULL' : null; - } + $placeholderValues = [ + '{type}' => $this->type, + '{length}' => $this->buildLengthString(), + '{notnull}' => $this->buildNotNullString(), + '{unique}' => $this->buildUniqueString(), + '{default}' => $this->buildDefaultString(), + '{check}' => $this->buildCheckString(), + '{comment}' => $this->buildCommentString(), + '{append}' => $this->buildAppendString(), + ]; - switch (gettype($this->default)) { - case 'double': - // ensure type cast always has . as decimal separator in all locales - $defaultValue = StringHelper::floatToString($this->default); - break; - case 'boolean': - $defaultValue = $this->default ? 'TRUE' : 'FALSE'; - break; - case 'integer': - case 'object': - $defaultValue = (string) $this->default; - break; - default: - $defaultValue = "'{$this->default}'"; - } + $placeholderValues += $customPlaceHolder; - return $defaultValue; + return strtr($format, $placeholderValues); } /** * Builds the default value specification for the column. + * * @return string string with default value of column. */ - protected function buildDefaultString() + protected function buildDefaultString(): string { $defaultValue = $this->buildDefaultValue(); + if ($defaultValue === null) { return ''; } @@ -393,94 +391,75 @@ protected function buildDefaultString() } /** - * Builds the check constraint for the column. - * @return string a string containing the CHECK constraint. + * Return the default value for the column. + * + * @return string|null string with default value of column. */ - protected function buildCheckString() + protected function buildDefaultValue(): string|null { - return $this->check !== null ? " CHECK ({$this->check})" : ''; - } + if ($this->default === null) { + return $this->isNotNull === false ? 'NULL' : null; + } - /** - * Builds the unsigned string for column. Defaults to unsupported. - * @return string a string containing UNSIGNED keyword. - * @since 2.0.7 - */ - protected function buildUnsignedString() - { - return ''; + return match (gettype($this->default)) { + // ensure type cast always has . as decimal separator in all locales + 'double' => StringHelper::floatToString($this->default), + 'boolean' => $this->default ? 'TRUE' : 'FALSE', + 'integer', 'object' => (string) $this->default, + default => "'{$this->default}'", + }; } /** - * Builds the after constraint for the column. Defaults to unsupported. - * @return string a string containing the AFTER constraint. - * @since 2.0.8 + * Builds the length/precision part of the column. + * + * @return string a string containing the length/precision of the column. */ - protected function buildAfterString() + protected function buildLengthString() { - return ''; - } + if ($this->length === null || $this->length === []) { + return ''; + } - /** - * Builds the first constraint for the column. Defaults to unsupported. - * @return string a string containing the FIRST constraint. - * @since 2.0.8 - */ - protected function buildFirstString() - { - return ''; - } + if (is_array($this->length)) { + $this->length = implode(',', $this->length); + } - /** - * Builds the custom string that's appended to column definition. - * @return string custom string to append. - * @since 2.0.9 - */ - protected function buildAppendString() - { - return $this->append !== null ? ' ' . $this->append : ''; + return "({$this->length})"; } /** - * Returns the category of the column type. - * @return string a string containing the column type category name. - * @since 2.0.8 + * Builds the not null constraint for the column. + * + * @return string returns 'NOT NULL' if [[isNotNull]] is true, 'NULL' if [[isNotNull]] is false or an empty string + * otherwise. */ - protected function getTypeCategory() + protected function buildNotNullString(): string { - return isset($this->categoryMap[$this->type]) ? $this->categoryMap[$this->type] : null; + return match ($this->isNotNull) { + true => ' NOT NULL', + false => ' NULL', + default => '', + }; } /** - * Builds the comment specification for the column. - * @return string a string containing the COMMENT keyword and the comment itself - * @since 2.0.8 + * Builds the unique constraint for the column. + * + * @return string returns string 'UNIQUE' if [[isUnique]] is true, otherwise it returns an empty string. */ - protected function buildCommentString() + protected function buildUniqueString(): string { - return ''; + return $this->isUnique ? ' UNIQUE' : ''; } /** - * Returns the complete column definition from input format. - * @param string $format the format of the definition. - * @return string a string containing the complete column definition. - * @since 2.0.8 + * Returns the category of the column type. + * + * @return string|null a string containing the column type category name. */ - protected function buildCompleteString($format) + protected function getTypeCategory(): string|null { - $placeholderValues = [ - '{type}' => $this->type, - '{length}' => $this->buildLengthString(), - '{unsigned}' => $this->buildUnsignedString(), - '{notnull}' => $this->buildNotNullString(), - '{unique}' => $this->buildUniqueString(), - '{default}' => $this->buildDefaultString(), - '{check}' => $this->buildCheckString(), - '{comment}' => $this->buildCommentString(), - '{pos}' => $this->isFirst ? $this->buildFirstString() : $this->buildAfterString(), - '{append}' => $this->buildAppendString(), - ]; - return strtr($format, $placeholderValues); + return isset($this->categoryMap[$this->type]) ? $this->categoryMap[$this->type] : null; } } diff --git a/src/db/Command.php b/src/db/Command.php index 5cb61a40..c93a2332 100644 --- a/src/db/Command.php +++ b/src/db/Command.php @@ -651,33 +651,37 @@ public function delete($table, $condition = '', $params = []) /** * Creates a SQL command for creating a new DB table. * - * The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'), - * where name stands for a column name which will be properly quoted by the method, and definition - * stands for the column type which must contain an abstract DB type. + * The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'), where name + * stands for a column name which will be properly quoted by the method, and definition stands for the column type + * which must contain an abstract DB type. * - * The method [[QueryBuilder::getColumnType()]] will be called - * to convert the abstract column types to physical ones. For example, `string` will be converted - * as `varchar(255)`, and `string not null` becomes `varchar(255) not null`. + * The method [[QueryBuilder::getColumnType()]] will be called to convert the abstract column types to physical + * ones. For example, `string` will be converted as `varchar(255)`, and `string not null` becomes + * `varchar(255) not null`. * - * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly - * inserted into the generated SQL. + * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly inserted + * into the generated SQL. * * Example usage: * ```php - * Yii::$app->db->createCommand()->createTable('post', [ - * 'id' => 'pk', - * 'title' => 'string', - * 'text' => 'text', - * 'column_name double precision null default null', - * ]); + * Yii::$app->db->createCommand()->createTable( + * 'post', + * [ + * 'id' => 'pk', + * 'title' => 'string', + * 'text' => 'text', + * 'column_name double precision null default null', + * ], + * ); * ``` * * @param string $table the name of the table to be created. The name will be properly quoted by the method. * @param array $columns the columns (name => definition) in the new table. * @param string|null $options additional SQL fragment that will be appended to the generated SQL. - * @return $this the command object itself + * + * @return static the command object itself. */ - public function createTable($table, $columns, $options = null) + public function createTable(string $table, array $columns, string|null $options = null): static { $sql = $this->db->getQueryBuilder()->createTable($table, $columns, $options); diff --git a/src/db/QueryBuilder.php b/src/db/QueryBuilder.php index e2b1df55..aac56914 100644 --- a/src/db/QueryBuilder.php +++ b/src/db/QueryBuilder.php @@ -783,33 +783,39 @@ public function delete($table, $condition, &$params) /** * Builds a SQL statement for creating a new DB table. * - * The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'), - * where name stands for a column name which will be properly quoted by the method, and definition - * stands for the column type which must contain an abstract DB type. + * The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'), where name + * stands for a column name which will be properly quoted by the method, and definition stands for the column type + * which must contain an abstract DB type. + * * The [[getColumnType()]] method will be invoked to convert any abstract type into a physical one. * - * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly - * inserted into the generated SQL. + * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly inserted + * into the generated SQL. * * For example, * * ```php - * $sql = $queryBuilder->createTable('user', [ - * 'id' => 'pk', - * 'name' => 'string', - * 'age' => 'integer', - * 'column_name double precision null default null', # definition only example - * ]); + * $sql = $queryBuilder->createTable( + * 'user', + * [ + * 'id' => 'pk', + * 'name' => 'string', + * 'age' => 'integer', + * 'column_name double precision null default null', # definition only example + * ], + * ); * ``` * * @param string $table the name of the table to be created. The name will be properly quoted by the method. * @param array $columns the columns (name => definition) in the new table. * @param string|null $options additional SQL fragment that will be appended to the generated SQL. + * * @return string the SQL statement for creating a new DB table. */ - public function createTable($table, $columns, $options = null) + public function createTable(string $table, array $columns, string|null $options = null): string { $cols = []; + foreach ($columns as $name => $type) { if (is_string($name)) { $cols[] = "\t" . $this->db->quoteColumnName($name) . ' ' . $this->getColumnType($type); @@ -817,6 +823,7 @@ public function createTable($table, $columns, $options = null) $cols[] = "\t" . $type; } } + $sql = 'CREATE TABLE ' . $this->db->quoteTableName($table) . " (\n" . implode(",\n", $cols) . "\n)"; return $options === null ? $sql : $sql . ' ' . $options; @@ -1264,9 +1271,12 @@ public function dropView($viewName) * The following abstract column types are supported (using MySQL as an example to explain the corresponding * physical types): * - * - `pk`: an auto-incremental primary key type, will be converted into "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY" - * - `bigpk`: an auto-incremental primary key type, will be converted into "bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY" - * - `upk`: an unsigned auto-incremental primary key type, will be converted into "int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY" + * - `pk`: an auto-incremental primary key type, will be converted into + * "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY" + * - `bigpk`: an auto-incremental primary key type, will be converted into + * "bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY" + * - `upk`: an unsigned auto-incremental primary key type, will be converted into + * "int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY" * - `char`: char type, will be converted into "char(1)" * - `string`: string type, will be converted into "varchar(255)" * - `text`: a long string type, will be converted into "text" @@ -1283,31 +1293,42 @@ public function dropView($viewName) * - `money`: money type, will be converted into "decimal(19,4)" * - `binary`: binary data type, will be converted into "blob" * - * If the abstract type contains two or more parts separated by spaces (e.g. "string NOT NULL"), then only - * the first part will be converted, and the rest of the parts will be appended to the converted result. + * If the abstract type contains two or more parts separated by spaces (e.g. "string NOT NULL"), then only the first + * part will be converted, and the rest of the parts will be appended to the converted result. + * * For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'. * - * For some of the abstract types you can also specify a length or precision constraint - * by appending it in round brackets directly to the type. + * For some of the abstract types you can also specify a length or precision constraint by appending it in round + * brackets directly to the type. + * * For example `string(32)` will be converted into "varchar(32)" on a MySQL database. + * * If the underlying DBMS does not support these kind of constraints for a type it will * be ignored. * * If a type cannot be found in [[typeMap]], it will be returned without any change. - * @param string|ColumnSchemaBuilder $type abstract column type + * + * @param string|ColumnSchemaBuilder $type abstract column type. + * * @return string physical column type. */ - public function getColumnType($type) + public function getColumnType(string|ColumnSchemaBuilder $type): string { - if ($type instanceof ColumnSchemaBuilder) { - $type = $type->__toString(); + if (is_string($type) === false) { + $type = (string) $type; } if (isset($this->typeMap[$type])) { return $this->typeMap[$type]; - } elseif (preg_match('/^(\w+)\((.+?)\)(.*)$/', $type, $matches)) { + } + + if (preg_match('/^(\w+)\((.+?)\)(.*)$/', $type, $matches)) { if (isset($this->typeMap[$matches[1]])) { - return preg_replace('/\(.+\)/', '(' . $matches[2] . ')', $this->typeMap[$matches[1]]) . $matches[3]; + return preg_replace( + '/\(.+\)/', + '(' . $matches[2] . ')', + $this->typeMap[$matches[1]] + ) . $matches[3]; } } elseif (preg_match('/^(\w+)\s+/', $type, $matches)) { if (isset($this->typeMap[$matches[1]])) { diff --git a/src/db/Schema.php b/src/db/Schema.php index 1f90c803..b9b84db2 100644 --- a/src/db/Schema.php +++ b/src/db/Schema.php @@ -20,108 +20,192 @@ * * Schema represents the database schema information that is DBMS specific. * - * @property-read string $lastInsertID The row ID of the last row inserted, or the last value retrieved from - * the sequence object. + * @property-read string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the + * sequence object. * @property-read QueryBuilder $queryBuilder The query builder for this connection. * @property-read string[] $schemaNames All schema names in the database, except system schemas. * @property-read string $serverVersion Server version as a string. * @property-read string[] $tableNames All table names in the database. - * @property-read TableSchema[] $tableSchemas The metadata for all tables in the database. Each array element - * is an instance of [[TableSchema]] or its child class. + * @property-read TableSchema[] $tableSchemas The metadata for all tables in the database. Each array element is an + * instance of [[TableSchema]] or its child class. * @property-write string $transactionIsolationLevel The transaction isolation level to use for this * transaction. This can be one of [[Transaction::READ_UNCOMMITTED]], [[Transaction::READ_COMMITTED]], * [[Transaction::REPEATABLE_READ]] and [[Transaction::SERIALIZABLE]] but also a string containing DBMS specific * syntax to be used after `SET TRANSACTION ISOLATION LEVEL`. - * - * @author Qiang Xue - * @author Sergey Makinen - * @since 2.0 */ abstract class Schema extends BaseObject { - // The following are the supported abstract column data types. - const TYPE_PK = 'pk'; - const TYPE_UPK = 'upk'; - const TYPE_BIGPK = 'bigpk'; - const TYPE_UBIGPK = 'ubigpk'; - const TYPE_CHAR = 'char'; - const TYPE_STRING = 'string'; - const TYPE_TEXT = 'text'; - const TYPE_TINYINT = 'tinyint'; - const TYPE_SMALLINT = 'smallint'; - const TYPE_INTEGER = 'integer'; - const TYPE_BIGINT = 'bigint'; - const TYPE_FLOAT = 'float'; - const TYPE_DOUBLE = 'double'; - const TYPE_DECIMAL = 'decimal'; - const TYPE_DATETIME = 'datetime'; - const TYPE_TIMESTAMP = 'timestamp'; - const TYPE_TIME = 'time'; - const TYPE_DATE = 'date'; - const TYPE_BINARY = 'binary'; - const TYPE_BOOLEAN = 'boolean'; - const TYPE_MONEY = 'money'; - const TYPE_JSON = 'json'; - /** - * Schema cache version, to detect incompatibilities in cached values when the - * data format of the cache changes. - */ - const SCHEMA_CACHE_VERSION = 1; - - /** - * @var Connection the database connection - */ - public $db; - /** - * @var string the default schema name used for the current session. - */ - public $defaultSchema; - /** - * @var array map of DB errors and corresponding exceptions + /** + * Define the abstract column type as an `integer` auto-incremental. + */ + public const TYPE_AUTO = 'auto'; + + /** + * Define the abstract column type as an `bigint` auto-incremental. + */ + public const TYPE_BIGAUTO = 'bigauto'; + + /** + * Define the abstract column type as an `integer` primary key. + */ + public const TYPE_PK = 'pk'; + + /** + * Define the abstract column type as an `bigint` primary key. + */ + public const TYPE_BIGPK = 'bigpk'; + + /** + * Define the abstract column type as `char`. + */ + public const TYPE_CHAR = 'char'; + + /** + * Define the abstract column type as `string`. + */ + public const TYPE_STRING = 'string'; + + /** + * Define the abstract column type as `text`. + */ + public const TYPE_TEXT = 'text'; + + /** + * Define the abstract column type as `smallint`. + */ + public const TYPE_TINYINT = 'tinyint'; + + /** + * Define the abstract column type as `smallint`. + */ + public const TYPE_SMALLINT = 'smallint'; + + /** + * Define the abstract column type as `integer`. + */ + public const TYPE_INTEGER = 'integer'; + + /** + * Define the abstract column type as `bigint`. + */ + public const TYPE_BIGINT = 'bigint'; + + /** + * Define the abstract column type as `float`. + */ + public const TYPE_FLOAT = 'float'; + + /** + * Define the abstract column type as `double`. + */ + public const TYPE_DOUBLE = 'double'; + + /** + * Define the abstract column type as `decimal`. + */ + public const TYPE_DECIMAL = 'decimal'; + + /** + * Define the abstract column type as `datetime`. + */ + public const TYPE_DATETIME = 'datetime'; + + /** + * Define the abstract column type as `timestamp`. + */ + public const TYPE_TIMESTAMP = 'timestamp'; + + /** + * Define the abstract column type as `time`. + */ + public const TYPE_TIME = 'time'; + + /** + * Define the abstract column type as `date`. + */ + public const TYPE_DATE = 'date'; + + /** + * Define the abstract column type as `binary`. + */ + public const TYPE_BINARY = 'binary'; + + /** + * Define the abstract column type as a `boolean`. + */ + public const TYPE_BOOLEAN = 'boolean'; + + /** + * Define the abstract column type as a `money`. + */ + public const TYPE_MONEY = 'money'; + + /** + * Define the abstract column type as `json` data. + */ + public const TYPE_JSON = 'json'; + + /** + * Schema cache version, to detect incompatibilities in cached values when the data format of the cache changes. + */ + public const SCHEMA_CACHE_VERSION = 1; + + /** + * @var Connection|null the database connection. + */ + public Connection|null $db = null; + + /** + * @var string|null the default schema name used for the current session. + */ + public string|null $defaultSchema = null; + + /** + * @var array map of DB errors and corresponding exceptions. * If left part is found in DB error message exception class from the right part is used. */ - public $exceptionMap = [ + public array $exceptionMap = [ 'SQLSTATE[23' => 'yii\db\IntegrityException', ]; + /** - * @var string|array column schema class or class config - * @since 2.0.11 + * @var string|array column schema class or class config. */ - public $columnSchemaClass = 'yii\db\ColumnSchema'; + public array|string $columnSchemaClass = 'yii\db\ColumnSchema'; /** * @var string|string[] character used to quote schema, table, etc. names. * An array of 2 characters can be used in case starting and ending characters are different. - * @since 2.0.14 */ protected array|string $tableQuoteCharacter = "'"; + /** * @var string|string[] character used to quote column names. * An array of 2 characters can be used in case starting and ending characters are different. - * @since 2.0.14 */ protected array|string $columnQuoteCharacter = '"'; /** - * @var array list of ALL schema names in the database, except system schemas + * @var array list of ALL schema names in the database, except system schemas. */ - private $_schemaNames; + private array $_schemaNames = []; + /** - * @var array list of ALL table names in the database + * @var array list of ALL table names in the database. */ - private $_tableNames = []; + private array $_tableNames = []; /** * @var array list of loaded table metadata (table name => metadata type => metadata). */ - private $_tableMetadata = []; + private array $_tableMetadata = []; /** - * @var QueryBuilder the query builder for this database + * @var QueryBuilder|null the query builder for this database */ - private $_builder; + private QueryBuilder|null $_builder = null; /** * @var string server version as a string. */ - private $_serverVersion; + private string|null $_serverVersion = null; /** @@ -206,14 +290,15 @@ public function getTableSchemas($schema = '', $refresh = false) /** * Returns all schema names in the database, except system schemas. - * @param bool $refresh whether to fetch the latest available schema names. If this is false, - * schema names fetched previously (if available) will be returned. + * + * @param bool $refresh whether to fetch the latest available schema names. If this is false, schema names fetched + * previously (if available) will be returned. + * * @return string[] all schema names in the database, except system schemas. - * @since 2.0.4 */ public function getSchemaNames($refresh = false) { - if ($this->_schemaNames === null || $refresh) { + if ($this->_schemaNames === [] || $refresh) { $this->_schemaNames = $this->findSchemaNames(); } @@ -320,14 +405,15 @@ public function createQueryBuilder() * * This method may be overridden by child classes to create a DBMS-specific column schema builder. * - * @param string $type type of the column. See [[ColumnSchemaBuilder::$type]]. + * @param string|null $type type of the column. See [[ColumnSchemaBuilder::$type]]. If null, returning instance may + * not be configured yet. * @param int|string|array|null $length length or precision of the column. See [[ColumnSchemaBuilder::$length]]. - * @return ColumnSchemaBuilder column schema builder instance - * @since 2.0.6 + * + * @return ColumnSchemaBuilder the column schema builder instance. */ - public function createColumnSchemaBuilder($type, $length = null) + public function createColumnSchemaBuilder(string|null $type = null, $length = null): ColumnSchemaBuilder { - return Yii::createObject(ColumnSchemaBuilder::class, [$type, $length]); + return Yii::createObject(ColumnSchemaBuilder::class, [$this->db, $type, $length]); } /** diff --git a/src/db/mssql/ColumnSchemaBuilder.php b/src/db/mssql/ColumnSchemaBuilder.php index 4523f332..e23ff97a 100644 --- a/src/db/mssql/ColumnSchemaBuilder.php +++ b/src/db/mssql/ColumnSchemaBuilder.php @@ -1,13 +1,10 @@ - * @since 2.0.42 */ -class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder +class ColumnSchemaBuilder extends \yii\db\ColumnSchemaBuilder { + /** + * @var string The format string for the column's schema. + */ protected $format = '{type}{length}{notnull}{unique}{default}{check}{append}'; + /** + * @var array The parameters for the IDENTITY column. + */ + private array $identityParams = []; + + public function __construct(Connection $db, string|null $type = null, $length = null, $config = []) + { + if (in_array($type, self::CATEGORY_GROUP_AUTO_PK, true) && is_array($length) && count($length) === 2) { + // start, increment. If increment is 0, it will be set to 1. + if ($length[1] === 0) { + $length[1] = 1; + } + + $this->identityParams = $length; + } + + parent::__construct($db, $type, $length, $config); + } /** * Builds the full string for the column's schema. + * * @return string */ - public function __toString() + public function __toString(): string { - if ($this->getTypeCategory() === self::CATEGORY_PK) { - $format = '{type}{check}{comment}{append}'; - } else { - $format = $this->format; + $format = $this->format; + + if (in_array($this->getTypeCategory(), self::CATEGORY_GROUP_AUTO_PK, true)) { + $format = '{type}{identity}{check}{comment}{append}'; } return $this->buildCompleteString($format); } + /** + * Creates an `IDENTITY` column. + * + * @param int|null $start The start value for the identity column. if null, it will be set to 1. + * @param int|null $increment The increment value for the identity column. if null, it will be set to 1. + * + * @return self The column schema builder instance. + */ + public function autoIncrement(int $start = null, int $increment = null): self + { + $this->type = self::CATEGORY_AUTO; + + if ($start !== null && $increment !== null) { + $this->identityParams = [$start, $increment === 0 ? 1 : $increment]; + } + + return $this; + } + + /** + * Creates a `BIGINT IDENTITY` column. + * + * @param int|null $start The start value for the identity column. if null, it will be set to 1. + * @param int|null $increment The increment value for the identity column. if null, it will be set to 1. + * + * @return self The column schema builder instance. + */ + public function bigAutoIncrement(int $start = null, int $increment = null): self + { + $this->type = self::CATEGORY_BIGAUTO; + + if ($start !== null && $increment !== null) { + $this->identityParams = [$start, $increment === 0 ? 1 : $increment]; + } + + return $this; + } + + /** + * Creates a `BIGINT IDENTITY PRIMARY KEY` column. + * + * @param int|null $start The start value for the identity column. if null, it will be set to 1. + * @param int|null $increment The increment value for the identity column. if null, it will be set to 1. + * + * @return self The column schema builder instance. + */ + public function bigPrimaryKey(int $start = null, int $increment = null): self + { + $this->type = self::CATEGORY_BIGPK; + + if ($start !== null && $increment !== null) { + $this->identityParams = [$start, $increment === 0 ? 1 : $increment]; + } + + return $this; + } + + /** + * Creates an `INTEGER IDENTITY PRIMARY KEY` column. + * + * @param int|null $start The start value for the identity column. if null, it will be set to 1. + * @param int|null $increment The increment value for the identity column. if null, it will be set to 1. + * + * @return self The column schema builder instance. + */ + public function primaryKey(int $start = null, int $increment = null): self + { + $this->type = self::CATEGORY_PK; + + if ($start !== null && $increment !== null) { + $this->identityParams = [$start, $increment === 0 ? 1 : $increment]; + } + + return $this; + } + /** * Changes default format string to MSSQL ALTER COMMAND. */ - public function setAlterColumnFormat() + public function setAlterColumnFormat(): void { $this->format = '{type}{length}{notnull}{append}'; } /** - * Getting the `Default` value for constraint + * Getting the `Default` value for constraint. + * * @return string|Expression|null default value of the column. */ - public function getDefaultValue() + public function getDefaultValue(): string|Expression|null { if ($this->default instanceof Expression) { return $this->default; @@ -61,10 +154,11 @@ public function getDefaultValue() } /** - * Get the `Check` value for constraint + * Get the `Check` value for constraint. + * * @return string|null the `CHECK` constraint for the column. */ - public function getCheckValue() + public function getCheckValue(): string|null { return $this->check !== null ? (string) $this->check : null; } @@ -72,8 +166,28 @@ public function getCheckValue() /** * @return bool whether the column values should be unique. If this is `true`, a `UNIQUE` constraint will be added. */ - public function isUnique() + public function isUnique(): bool { return $this->isUnique; } + + /** + * {@inheritdoc} + */ + protected function buildCompleteString(string $format, array $customPlaceHolder = []): string + { + return parent::buildCompleteString($format, ['{identity}' => $this->buildIdentityString()]); + } + + /** + * {@inheritdoc} + */ + protected function buildIdentityString(): string + { + if (in_array($this->getTypeCategory(), self::CATEGORY_GROUP_AUTO_PK, true) && $this->identityParams !== []) { + return "({$this->identityParams[0]},{$this->identityParams[1]})"; + } + + return ''; + } } diff --git a/src/db/mssql/QueryBuilder.php b/src/db/mssql/QueryBuilder.php index 4804ad60..b5f9a523 100644 --- a/src/db/mssql/QueryBuilder.php +++ b/src/db/mssql/QueryBuilder.php @@ -6,12 +6,13 @@ use yii\base\InvalidArgumentException; use yii\base\NotSupportedException; +use yii\db\ColumnSchemaBuilder; use yii\db\Expression; use yii\db\ExpressionInterface; use yii\db\QueryInterface; /** - * QueryBuilder is the query builder for MS SQL Server databases (version 2008 and above). + * QueryBuilder is the query builder for MS SQL Server databases (version 2012 and above). */ class QueryBuilder extends \yii\db\QueryBuilder { @@ -19,26 +20,43 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = [ + // auto-incremental + Schema::TYPE_AUTO => 'int IDENTITY', + Schema::TYPE_BIGAUTO => 'bigint IDENTITY', + + // primary key Schema::TYPE_PK => 'int IDENTITY PRIMARY KEY', - Schema::TYPE_UPK => 'int IDENTITY PRIMARY KEY', Schema::TYPE_BIGPK => 'bigint IDENTITY PRIMARY KEY', - Schema::TYPE_UBIGPK => 'bigint IDENTITY PRIMARY KEY', + + // string types Schema::TYPE_CHAR => 'nchar(1)', Schema::TYPE_STRING => 'nvarchar(255)', Schema::TYPE_TEXT => 'nvarchar(max)', + + // integers Schema::TYPE_TINYINT => 'tinyint', Schema::TYPE_SMALLINT => 'smallint', Schema::TYPE_INTEGER => 'int', Schema::TYPE_BIGINT => 'bigint', + + // decimals Schema::TYPE_FLOAT => 'float', Schema::TYPE_DOUBLE => 'float', Schema::TYPE_DECIMAL => 'decimal(18,0)', + + // dates Schema::TYPE_DATETIME => 'datetime', Schema::TYPE_TIMESTAMP => 'datetime', Schema::TYPE_TIME => 'time', Schema::TYPE_DATE => 'date', + + // binary Schema::TYPE_BINARY => 'varbinary(max)', + + // boolean Schema::TYPE_BOOLEAN => 'bit', + + // money Schema::TYPE_MONEY => 'decimal(19,4)', ]; @@ -580,14 +598,13 @@ public function update($table, $columns, $condition, &$params) /** * {@inheritdoc} */ - public function getColumnType($type) + public function getColumnType(string|ColumnSchemaBuilder $type): string { $columnType = parent::getColumnType($type); - // remove unsupported keywords - $columnType = preg_replace("/\s*comment '.*'/i", '', $columnType); - $columnType = preg_replace('/ first$/i', '', $columnType); - return $columnType; + $columnType = $this->handleIdentityColumn($columnType, (string) $type); + + return preg_replace(["/\s*comment '.*'/i", '/ first$/i', '/\s+unsigned/i'], '', $columnType); } /** @@ -646,4 +663,33 @@ public function dropColumn($table, $column) return $this->dropConstraintsForColumn($table, $column) . "\nALTER TABLE " . $this->db->quoteTableName($table) . ' DROP COLUMN ' . $this->db->quoteColumnName($column); } + + private function handleIdentityColumn(string $columnType, string $type): string + { + if (preg_match('/^(big)?(auto|pk)\s*\(([-]?\d+),(\d+)\)$/i', $type, $matches)) { + $isBig = !empty($matches[1]); + $isPk = strtolower($matches[2]) === 'pk'; + $start = $matches[3]; // Ahora puede ser negativo + $increment = ($matches[4] == '0') ? '1' : $matches[4]; + $dataType = $isBig ? 'bigint' : 'int'; + + return "$dataType IDENTITY($start,$increment)" . ($isPk ? ' PRIMARY KEY' : ''); + } + + if (preg_match('/^(big)?int IDENTITY\(([-]?\d+)\)(\s+PRIMARY KEY)?$/i', $columnType, $matches)) { + $dataType = !empty($matches[1]) ? 'bigint' : 'int'; + $isPk = !empty($matches[3]); + + return "$dataType IDENTITY" . ($isPk ? ' PRIMARY KEY' : ''); + } + + if (preg_match('/IDENTITY\(([-]?\d+),(\d+)\)/i', $columnType, $matches)) { + $start = $matches[1]; // Ahora puede ser negativo + $increment = ($matches[2] == '0') ? '1' : $matches[2]; + + return preg_replace('/IDENTITY\(([-]?\d+),\d+\)/i', "IDENTITY($start,$increment)", $columnType); + } + + return $columnType; + } } diff --git a/src/db/mssql/Schema.php b/src/db/mssql/Schema.php index 8f3969d2..f6eb842d 100644 --- a/src/db/mssql/Schema.php +++ b/src/db/mssql/Schema.php @@ -1,9 +1,6 @@ - * @since 2.0 + * Schema is the class for retrieving metadata from MS SQL Server databases (version 2012 and above). */ class Schema extends \yii\db\Schema implements ConstraintFinderInterface { @@ -32,15 +26,17 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface /** * {@inheritdoc} */ - public $columnSchemaClass = 'yii\db\mssql\ColumnSchema'; + public array|string $columnSchemaClass = 'yii\db\mssql\ColumnSchema'; + /** - * @var string the default schema used for the current session. + * @var string|null the default schema used for the current session. */ - public $defaultSchema = 'dbo'; + public string|null $defaultSchema = 'dbo'; + /** * @var array mapping from physical column types (keys) to abstract column types (values) */ - public $typeMap = [ + public array $typeMap = [ // exact numbers 'bigint' => self::TYPE_BIGINT, 'numeric' => self::TYPE_DECIMAL, @@ -51,10 +47,12 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface 'int' => self::TYPE_INTEGER, 'tinyint' => self::TYPE_TINYINT, 'money' => self::TYPE_MONEY, + // approximate numbers 'float' => self::TYPE_FLOAT, 'double' => self::TYPE_DOUBLE, 'real' => self::TYPE_FLOAT, + // date and time 'date' => self::TYPE_DATE, 'datetimeoffset' => self::TYPE_DATETIME, @@ -62,20 +60,23 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface 'smalldatetime' => self::TYPE_DATETIME, 'datetime' => self::TYPE_DATETIME, 'time' => self::TYPE_TIME, + // character strings 'char' => self::TYPE_CHAR, 'varchar' => self::TYPE_STRING, 'text' => self::TYPE_TEXT, + // unicode character strings 'nchar' => self::TYPE_CHAR, 'nvarchar' => self::TYPE_STRING, 'ntext' => self::TYPE_TEXT, + // binary strings 'binary' => self::TYPE_BINARY, 'varbinary' => self::TYPE_BINARY, 'image' => self::TYPE_BINARY, - // other data types - // 'cursor' type cannot be used with tables + + // other data types, 'cursor' type cannot be used with tables 'timestamp' => self::TYPE_TIMESTAMP, 'hierarchyid' => self::TYPE_STRING, 'uniqueidentifier' => self::TYPE_STRING, @@ -332,7 +333,7 @@ public function rollBackSavepoint($name) */ public function createQueryBuilder() { - return Yii::createObject(QueryBuilder::className(), [$this->db]); + return Yii::createObject(QueryBuilder::class, [$this->db]); } /** @@ -778,8 +779,8 @@ public function quoteColumnName(string $name): string /** * {@inheritdoc} */ - public function createColumnSchemaBuilder($type, $length = null) + public function createColumnSchemaBuilder(string|null $type = null, $length = null): ColumnSchemaBuilder { - return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length, $this->db]); + return Yii::createObject(ColumnSchemaBuilder::class, [$this->db, $type, $length]); } } diff --git a/src/db/mysql/ColumnSchemaBuilder.php b/src/db/mysql/ColumnSchemaBuilder.php index dbf507e0..34ac311e 100644 --- a/src/db/mysql/ColumnSchemaBuilder.php +++ b/src/db/mysql/ColumnSchemaBuilder.php @@ -1,26 +1,175 @@ - * @since 2.0.8 */ -class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder +class ColumnSchemaBuilder extends \yii\db\ColumnSchemaBuilder { + /** + * Abstract column type for `UNSIGNED BIGINT AUTO_INCREMENT PRIMARY KEY` columns. + */ + public const CATEGORY_UBIGPK = 'ubigpk'; + + /** + * Abstract column type for `UNSIGNED AUTO_INCREMENT PRIMARY KEY` columns. + */ + public const CATEGORY_UPK = 'upk'; + + /** + * @var string|null the column after which this column will be added. + */ + protected string|null $after = null; + + /** + * @var bool whether this column is to be inserted at the beginning of the table. + */ + protected bool $isFirst = false; + + /** + * @var bool whether the column values should be unsigned. If this is `true`, an `UNSIGNED` keyword will be added. + */ + protected bool $isUnsigned = false; + + /** + * {@inheritdoc} + */ + public function __toString(): string + { + $format = match ($this->getTypeCategory()) { + self::CATEGORY_PK => '{type}{length}{comment}{check}{append}{pos}', + self::CATEGORY_NUMERIC => '{type}{length}{unsigned}{notnull}{default}{unique}{comment}{append}{pos}{check}', + default => '{type}{length}{notnull}{default}{unique}{comment}{append}{pos}{check}', + }; + + return $this->buildCompleteString($format); + } + + /** + * Adds an `AFTER` constraint to the column. + * + * @param string $after the column after which $this column will be added. + * + * @return static Instance of the column schema builder. + */ + public function after(string $after): static + { + $this->after = $after; + + return $this; + } + + /** + * Creates an `INTEGER AUTO_INCREMENT` column. + * + * @param int|null $length column size or precision definition. + * + * @return self The column schema builder instance. + */ + public function autoIncrement(int|null $length = null): self + { + $this->type = self::CATEGORY_AUTO; + + if ($length !== null) { + $this->length = $length; + } + + return $this; + } + + /** + * Creates a `BIGINT AUTO_INCREMENT` column. + * + * @param int|null $length column size or precision definition. Defaults to 20. + * + * @return self The column schema builder instance. + */ + public function bigAutoIncrement(int|null $length = null): self + { + $this->type = self::CATEGORY_BIGAUTO; + + if ($length !== null) { + $this->length = $length; + } + + return $this; + } + + /** + * Creates a `BIGINT AUTO_INCREMENT PRIMARY KEY` column. + * + * @param int|null $length column size or precision definition. Defaults to 20. + * + * @return self The column schema builder instance. + */ + public function bigPrimaryKey(int|null $length = null): self + { + $this->type = self::CATEGORY_BIGPK; + + if ($length !== null) { + $this->length = $length; + } + + return $this; + } + + /** + * Adds an `FIRST` constraint to the column. + * + * @return static Instance of the column schema builder. + */ + public function first(): static + { + $this->isFirst = true; + + return $this; + } + + /** + * Creates a `INTEGER AUTO_INCREMENT PRIMARY KEY` column. + * + * @param int|null $length column size or precision definition. Defaults to 11. + * + * @return self The column schema builder instance. + */ + public function primaryKey(int|null $length = null): self + { + $this->type = self::CATEGORY_PK; + + if ($length !== null) { + $this->length = $length; + } + + return $this; + } + + /** + * Marks column as unsigned. + * + * @return static Instance of the column schema builder. + */ + public function unsigned(): static + { + $this->type = match ($this->type) { + Schema::TYPE_AUTO => Schema::TYPE_UAUTO, + Schema::TYPE_BIGAUTO => Schema::TYPE_UBIGAUTO, + Schema::TYPE_PK => Schema::TYPE_UPK, + Schema::TYPE_BIGPK => Schema::TYPE_UBIGPK, + default => $this->type, + }; + + $this->isUnsigned = true; + + return $this; + } + /** * {@inheritdoc} */ - protected function buildUnsignedString() + protected function buildUnsignedString(): string { return $this->isUnsigned ? ' UNSIGNED' : ''; } @@ -28,17 +177,15 @@ protected function buildUnsignedString() /** * {@inheritdoc} */ - protected function buildAfterString() + protected function buildAfterString(): string { - return $this->after !== null ? - ' AFTER ' . $this->db->quoteColumnName($this->after) : - ''; + return $this->after !== null ? ' AFTER ' . $this->db->quoteColumnName($this->after) : ''; } /** * {@inheritdoc} */ - protected function buildFirstString() + protected function buildFirstString(): string { return $this->isFirst ? ' FIRST' : ''; } @@ -46,27 +193,26 @@ protected function buildFirstString() /** * {@inheritdoc} */ - protected function buildCommentString() + protected function buildCommentString(): string { return $this->comment !== null ? ' COMMENT ' . $this->db->quoteValue($this->comment) : ''; } /** - * {@inheritdoc} + * Returns the complete column definition from input format. + * + * @param string $format the format of the definition. + * + * @return string a string containing the complete column definition. */ - public function __toString() + protected function buildCompleteString(string $format, array $customPlaceHolder = []): string { - switch ($this->getTypeCategory()) { - case self::CATEGORY_PK: - $format = '{type}{length}{comment}{check}{append}{pos}'; - break; - case self::CATEGORY_NUMERIC: - $format = '{type}{length}{unsigned}{notnull}{default}{unique}{comment}{append}{pos}{check}'; - break; - default: - $format = '{type}{length}{notnull}{default}{unique}{comment}{append}{pos}{check}'; - } - - return $this->buildCompleteString($format); + return parent::buildCompleteString( + $format, + [ + '{unsigned}' => $this->buildUnsignedString(), + '{pos}' => $this->isFirst ? $this->buildFirstString() : $this->buildAfterString(), + ], + ); } } diff --git a/src/db/mysql/QueryBuilder.php b/src/db/mysql/QueryBuilder.php index 4cdf298f..c3b7a063 100644 --- a/src/db/mysql/QueryBuilder.php +++ b/src/db/mysql/QueryBuilder.php @@ -15,9 +15,6 @@ /** * QueryBuilder is the query builder for MySQL databases. - * - * @author Qiang Xue - * @since 2.0 */ class QueryBuilder extends \yii\db\QueryBuilder { @@ -25,10 +22,22 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = [ - Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', - Schema::TYPE_UPK => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + // auto-incremental + Schema::TYPE_AUTO => 'int(11) AUTO_INCREMENT', + Schema::TYPE_BIGAUTO => 'bigint(20) AUTO_INCREMENT', + + // auto-incremental unsigned + Schema::TYPE_UAUTO => 'int(10) UNSIGNED AUTO_INCREMENT', + Schema::TYPE_UBIGAUTO => 'bigint(20) UNSIGNED AUTO_INCREMENT', + + // primary key Schema::TYPE_BIGPK => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY', + Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', + + // primary key unsigned Schema::TYPE_UBIGPK => 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + Schema::TYPE_UPK => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + Schema::TYPE_CHAR => 'char(1)', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', @@ -46,7 +55,6 @@ class QueryBuilder extends \yii\db\QueryBuilder Schema::TYPE_JSON => 'json' ]; - /** * {@inheritdoc} */ @@ -67,6 +75,73 @@ protected function defaultExpressionBuilders() ]); } + /** + * Builds a SQL statement for creating a new DB table. + * + * The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'), where name + * stands for a column name which will be properly quoted by the method, and definition stands for the column type + * which must contain an abstract DB type. + * + * The [[getColumnType()]] method will be invoked to convert any abstract type into a physical one. + * + * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly inserted + * into the generated SQL. + * + * For example, + * + * ```php + * $sql = $queryBuilder->createTable( + * 'user', + * [ + * 'id' => 'pk', + * 'name' => 'string', + * 'age' => 'integer', + * 'column_name double precision null default null', # definition only example + * ], + * ); + * ``` + * + * Note: When column definition is specified as auto-incremental, the column will be set as key automatically. + * + * @param string $table the name of the table to be created. The name will be properly quoted by the method. + * @param array $columns the columns (name => definition) in the new table. + * @param string|null $options additional SQL fragment that will be appended to the generated SQL. + * + * @return string the SQL statement for creating a new DB table. + */ + public function createTable(string $table, array $columns, string|null $options = null): string + { + $autoIncrementColumn = null; + $hasPrimaryKey = false; + + $cols = []; + + foreach ($columns as $name => $type) { + if (is_string($name)) { + $columnType = $this->getColumnType($type); + $cols[] = "\t" . $this->db->quoteColumnName($name) . ' ' . $columnType; + + if (stripos($columnType, 'AUTO_INCREMENT') !== false) { + $autoIncrementColumn = $name; + } + + if (stripos($columnType, 'PRIMARY KEY') !== false) { + $hasPrimaryKey = true; + } + } else { + $cols[] = "\t" . $type; + } + } + + if ($autoIncrementColumn !== null && $hasPrimaryKey === false) { + $cols[] = 'PRIMARY KEY (' . $this->db->quoteColumnName($autoIncrementColumn) . ')'; + } + + $sql = 'CREATE TABLE ' . $this->db->quoteTableName($table) . " (\n" . implode(",\n", $cols) . "\n)"; + + return $options === null ? $sql : $sql . ' ' . $options; + } + /** * Builds a SQL statement for renaming a column. * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method. diff --git a/src/db/mysql/Schema.php b/src/db/mysql/Schema.php index cec8aea9..bba0e2ef 100644 --- a/src/db/mysql/Schema.php +++ b/src/db/mysql/Schema.php @@ -1,9 +1,6 @@ - * @since 2.0 + * Schema is the class for retrieving metadata from a MySQL database (5.6.x, 5.7.x and 8.x). */ class Schema extends \yii\db\Schema implements ConstraintFinderInterface { use ConstraintFinderTrait; /** - * {@inheritdoc} + * Define the abstract column type as an `integer UNSIGNED` auto-incremental. + */ + public const TYPE_UAUTO = 'uauto'; + + /** + * Define the abstract column type as an `bigint UNSIGNED` auto-incremental. + */ + public const TYPE_UBIGAUTO = 'ubigauto'; + + /** + * Define the abstract column type as an `integer UNSIGNED` primary key. */ - public $columnSchemaClass = 'yii\db\mysql\ColumnSchema'; + public const TYPE_UPK = 'upk'; + /** - * @var bool whether MySQL used is older than 5.1. + * Define the abstract column type as an `bigint UNSIGNED` primary key. */ - private $_oldMysql; + public const TYPE_UBIGPK = 'ubigpk'; + /** + * {@inheritdoc} + */ + public array|string $columnSchemaClass = 'yii\db\mysql\ColumnSchema'; /** * @var array mapping from physical column types (keys) to abstract column types (values) */ - public $typeMap = [ + public array $typeMap = [ 'tinyint' => self::TYPE_TINYINT, 'bool' => self::TYPE_TINYINT, 'boolean' => self::TYPE_TINYINT, @@ -87,11 +96,17 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface * {@inheritdoc} */ protected array|string $tableQuoteCharacter = '`'; + /** * {@inheritdoc} */ protected array|string $columnQuoteCharacter = '`'; + /** + * @var bool|null whether MySQL used is older than 5.1. + */ + private bool|null $_oldMysql = null; + /** * {@inheritdoc} */ @@ -517,9 +532,9 @@ public function findUniqueIndexes($table) /** * {@inheritdoc} */ - public function createColumnSchemaBuilder($type, $length = null) + public function createColumnSchemaBuilder(string|null $type = null, $length = null): ColumnSchemaBuilder { - return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length, $this->db]); + return Yii::createObject(ColumnSchemaBuilder::class, [$this->db, $type, $length]); } /** diff --git a/src/db/oci/ColumnSchemaBuilder.php b/src/db/oci/ColumnSchemaBuilder.php index dcb5ff21..a8c737ea 100644 --- a/src/db/oci/ColumnSchemaBuilder.php +++ b/src/db/oci/ColumnSchemaBuilder.php @@ -1,9 +1,6 @@ - * @author Chris Harris - * @since 2.0.6 */ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder { /** * {@inheritdoc} */ - protected function buildUnsignedString() - { - return $this->isUnsigned ? ' UNSIGNED' : ''; - } - - /** - * {@inheritdoc} - */ - public function __toString() + public function __toString(): string { - switch ($this->getTypeCategory()) { - case self::CATEGORY_PK: - $format = '{type}{length}{check}{append}'; - break; - case self::CATEGORY_NUMERIC: - $format = '{type}{length}{unsigned}{default}{notnull}{check}{append}'; - break; - default: - $format = '{type}{length}{default}{notnull}{check}{append}'; - } + $format = match ($this->getTypeCategory()) { + self::CATEGORY_PK => '{type}{length}{check}{append}', + self::CATEGORY_NUMERIC => '{type}{length}{default}{notnull}{check}{append}', + default => '{type}{length}{default}{notnull}{check}{append}', + }; return $this->buildCompleteString($format); } diff --git a/src/db/oci/QueryBuilder.php b/src/db/oci/QueryBuilder.php index 475d0f6b..11c0a47f 100644 --- a/src/db/oci/QueryBuilder.php +++ b/src/db/oci/QueryBuilder.php @@ -23,10 +23,10 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = [ + // primary key Schema::TYPE_PK => 'NUMBER(10) NOT NULL PRIMARY KEY', - Schema::TYPE_UPK => 'NUMBER(10) UNSIGNED NOT NULL PRIMARY KEY', Schema::TYPE_BIGPK => 'NUMBER(20) NOT NULL PRIMARY KEY', - Schema::TYPE_UBIGPK => 'NUMBER(20) UNSIGNED NOT NULL PRIMARY KEY', + Schema::TYPE_CHAR => 'CHAR(1)', Schema::TYPE_STRING => 'VARCHAR2(255)', Schema::TYPE_TEXT => 'CLOB', diff --git a/src/db/oci/Schema.php b/src/db/oci/Schema.php index 44f277b4..02732d92 100644 --- a/src/db/oci/Schema.php +++ b/src/db/oci/Schema.php @@ -1,9 +1,6 @@ - * @since 2.0 */ class Schema extends \yii\db\Schema implements ConstraintFinderInterface { @@ -38,12 +32,13 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface /** * {@inheritdoc} */ - public $columnSchemaClass = 'yii\db\oci\ColumnSchema'; + public array|string $columnSchemaClass = 'yii\db\oci\ColumnSchema'; + /** * @var array map of DB errors and corresponding exceptions * If left part is found in DB error message exception class from the right part is used. */ - public $exceptionMap = [ + public array $exceptionMap = [ 'ORA-00001: unique constraint' => 'yii\db\IntegrityException', ]; @@ -52,7 +47,6 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface */ protected array|string $tableQuoteCharacter = '"'; - /** * {@inheritdoc} */ @@ -272,9 +266,9 @@ public function createQueryBuilder() /** * {@inheritdoc} */ - public function createColumnSchemaBuilder($type, $length = null) + public function createColumnSchemaBuilder(string|null $type = null, $length = null): ColumnSchemaBuilder { - return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length]); + return Yii::createObject(ColumnSchemaBuilder::class, [$this->db, $type, $length]); } /** diff --git a/src/db/pgsql/QueryBuilder.php b/src/db/pgsql/QueryBuilder.php index eb055461..f6666788 100644 --- a/src/db/pgsql/QueryBuilder.php +++ b/src/db/pgsql/QueryBuilder.php @@ -48,10 +48,10 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = [ + // primary key Schema::TYPE_PK => 'serial NOT NULL PRIMARY KEY', - Schema::TYPE_UPK => 'serial NOT NULL PRIMARY KEY', Schema::TYPE_BIGPK => 'bigserial NOT NULL PRIMARY KEY', - Schema::TYPE_UBIGPK => 'bigserial NOT NULL PRIMARY KEY', + Schema::TYPE_CHAR => 'char(1)', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', diff --git a/src/db/pgsql/Schema.php b/src/db/pgsql/Schema.php index c51528c8..8dd97fc3 100644 --- a/src/db/pgsql/Schema.php +++ b/src/db/pgsql/Schema.php @@ -1,9 +1,6 @@ - * @since 2.0 + * Schema is the class for retrieving metadata from a PostgreSQL database (version 9.x and above). */ class Schema extends \yii\db\Schema implements ConstraintFinderInterface { use ViewFinderTrait; use ConstraintFinderTrait; - const TYPE_JSONB = 'jsonb'; + /** + * Define the abstract column type as `jsonb`. + */ + public const TYPE_JSONB = 'jsonb'; /** - * @var string the default schema used for the current session. + * @var string|null the default schema used for the current session. */ - public $defaultSchema = 'public'; + public string|null $defaultSchema = 'public'; + /** * {@inheritdoc} */ - public $columnSchemaClass = 'yii\db\pgsql\ColumnSchema'; + public array|string $columnSchemaClass = 'yii\db\pgsql\ColumnSchema'; + /** - * @var array mapping from physical column types (keys) to abstract - * column types (values) + * @var array mapping from physical column types (keys) to abstract column types (values). + * * @see https://www.postgresql.org/docs/current/datatype.html#DATATYPE-TABLE */ - public $typeMap = [ + public array $typeMap = [ 'bit' => self::TYPE_INTEGER, 'bit varying' => self::TYPE_INTEGER, 'varbit' => self::TYPE_INTEGER, @@ -131,7 +129,6 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface */ protected array|string $tableQuoteCharacter = '"'; - /** * {@inheritdoc} */ diff --git a/src/db/sqlite/ColumnSchemaBuilder.php b/src/db/sqlite/ColumnSchemaBuilder.php index d9853b08..fd5ef61a 100644 --- a/src/db/sqlite/ColumnSchemaBuilder.php +++ b/src/db/sqlite/ColumnSchemaBuilder.php @@ -1,26 +1,41 @@ - * @since 2.0.8 */ -class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder +class ColumnSchemaBuilder extends \yii\db\ColumnSchemaBuilder { + /** + * @var bool whether the column values should be unsigned. If this is `true`, an `UNSIGNED` keyword will be added. + */ + protected bool $isUnsigned = false; + + /** + * Marks column as unsigned. + * + * @return static Instance of the column schema builder. + */ + public function unsigned(): static + { + $this->type = match ($this->type) { + Schema::TYPE_PK => Schema::TYPE_UPK, + Schema::TYPE_BIGPK => Schema::TYPE_UBIGPK, + default => $this->type, + }; + + $this->isUnsigned = true; + + return $this; + } + /** * {@inheritdoc} */ - protected function buildUnsignedString() + protected function buildUnsignedString(): string { return $this->isUnsigned ? ' UNSIGNED' : ''; } @@ -30,17 +45,15 @@ protected function buildUnsignedString() */ public function __toString() { - switch ($this->getTypeCategory()) { - case self::CATEGORY_PK: - $format = '{type}{check}{append}'; - break; - case self::CATEGORY_NUMERIC: - $format = '{type}{length}{unsigned}{notnull}{unique}{check}{default}{append}'; - break; - default: - $format = '{type}{length}{notnull}{unique}{check}{default}{append}'; - } - - return $this->buildCompleteString($format); + $format = match ($this->getTypeCategory()) { + self::CATEGORY_PK => '{type}{check}{append}', + self::CATEGORY_NUMERIC => '{type}{length}{unsigned}{notnull}{unique}{check}{default}{append}', + default => '{type}{length}{notnull}{unique}{check}{default}{append}', + }; + + return $this->buildCompleteString( + $format, + ['{unsigned}' => $this->buildUnsignedString()], + ); } } diff --git a/src/db/sqlite/QueryBuilder.php b/src/db/sqlite/QueryBuilder.php index 53071acf..a6646e5f 100644 --- a/src/db/sqlite/QueryBuilder.php +++ b/src/db/sqlite/QueryBuilder.php @@ -29,10 +29,14 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = [ + // primary key Schema::TYPE_PK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', - Schema::TYPE_UPK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', Schema::TYPE_BIGPK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', - Schema::TYPE_UBIGPK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', + + // unsigned primary + Schema::TYPE_UPK => 'integer UNSIGNED PRIMARY KEY AUTOINCREMENT NOT NULL', + Schema::TYPE_UBIGPK => 'integer UNSIGNED PRIMARY KEY AUTOINCREMENT NOT NULL', + Schema::TYPE_CHAR => 'char(1)', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', diff --git a/src/db/sqlite/Schema.php b/src/db/sqlite/Schema.php index df087c41..93131079 100644 --- a/src/db/sqlite/Schema.php +++ b/src/db/sqlite/Schema.php @@ -1,9 +1,6 @@ - * @since 2.0 + * @property-write string $transactionIsolationLevel The transaction isolation level to use for this transaction. + * This can be either [[Transaction::READ_UNCOMMITTED]] or [[Transaction::SERIALIZABLE]]. */ class Schema extends \yii\db\Schema implements ConstraintFinderInterface { use ConstraintFinderTrait; /** - * @var array mapping from physical column types (keys) to abstract column types (values) + * Define the abstract column type as an `integer UNSIGNED` auto-incremental. */ - public $typeMap = [ + public const TYPE_UAUTO = 'uauto'; + + /** + * Define the abstract column type as an `bigint UNSIGNED` auto-incremental. + */ + public const TYPE_UBIGAUTO = 'ubigauto'; + + /** + * Define the abstract column type as an `integer UNSIGNED` primary key. + */ + public const TYPE_UPK = 'upk'; + + /** + * Define the abstract column type as an `bigint UNSIGNED` primary key. + */ + public const TYPE_UBIGPK = 'ubigpk'; + + /** + * @var array mapping from physical column types (keys) to abstract column types (values). + */ + public array $typeMap = [ 'tinyint' => self::TYPE_TINYINT, 'bit' => self::TYPE_SMALLINT, 'boolean' => self::TYPE_BOOLEAN, @@ -78,7 +92,6 @@ class Schema extends \yii\db\Schema implements ConstraintFinderInterface */ protected array|string $columnQuoteCharacter = '`'; - /** * {@inheritdoc} */ @@ -212,11 +225,10 @@ public function createQueryBuilder() /** * {@inheritdoc} - * @return ColumnSchemaBuilder column schema builder instance */ - public function createColumnSchemaBuilder($type, $length = null) + public function createColumnSchemaBuilder(string|null $type = null, $length = null): ColumnSchemaBuilder { - return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length]); + return Yii::createObject(ColumnSchemaBuilder::class, [$this->db, $type, $length]); } /** diff --git a/tests/framework/db/ColumnSchemaBuilderTest.php b/tests/framework/db/ColumnSchemaBuilderTest.php index 43cec278..33913fd0 100644 --- a/tests/framework/db/ColumnSchemaBuilderTest.php +++ b/tests/framework/db/ColumnSchemaBuilderTest.php @@ -20,7 +20,7 @@ abstract class ColumnSchemaBuilderTest extends DatabaseTestCase */ public function getColumnSchemaBuilder($type, $length = null) { - return new ColumnSchemaBuilder($type, $length, $this->getConnection()); + return new ColumnSchemaBuilder($this->getConnection(), $type, $length); } /** @@ -29,12 +29,6 @@ public function getColumnSchemaBuilder($type, $length = null) public function typesProvider() { return [ - ['integer NULL DEFAULT NULL', Schema::TYPE_INTEGER, null, [ - ['unsigned'], ['null'], - ]], - ['integer(10)', Schema::TYPE_INTEGER, 10, [ - ['unsigned'], - ]], ['timestamp() WITH TIME ZONE NOT NULL', 'timestamp() WITH TIME ZONE', null, [ ['notNull'], ]], @@ -68,6 +62,7 @@ public function testCustomTypes($expected, $type, $length, $calls) public function checkBuildString($expected, $type, $length, $calls) { $builder = $this->getColumnSchemaBuilder($type, $length); + foreach ($calls as $call) { $method = array_shift($call); \call_user_func_array([$builder, $method], $call); diff --git a/tests/framework/db/QueryBuilderTest.php b/tests/framework/db/QueryBuilderTest.php index 3a897f8d..13ea72b6 100644 --- a/tests/framework/db/QueryBuilderTest.php +++ b/tests/framework/db/QueryBuilderTest.php @@ -603,7 +603,8 @@ public function columnTypes() ], [ Schema::TYPE_TINYINT . ' UNSIGNED', - $this->tinyInteger()->unsigned(), + in_array($this->driverName, ['mysql', 'sqlite'], true) + ? $this->tinyInteger()->unsigned() : $this->tinyInteger(), [ 'mysql' => 'tinyint(3) UNSIGNED', 'sqlite' => 'tinyint UNSIGNED', @@ -865,24 +866,6 @@ public function columnTypes() 'sqlsrv' => 'datetime NULL DEFAULT NULL', ], ], - [ - Schema::TYPE_UPK, - $this->primaryKey()->unsigned(), - [ - 'mysql' => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', - 'pgsql' => 'serial NOT NULL PRIMARY KEY', - 'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', - ], - ], - [ - Schema::TYPE_UBIGPK, - $this->bigPrimaryKey()->unsigned(), - [ - 'mysql' => 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', - 'pgsql' => 'bigserial NOT NULL PRIMARY KEY', - 'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', - ], - ], [ Schema::TYPE_INTEGER . " COMMENT 'test comment'", $this->integer()->comment('test comment'), @@ -899,7 +882,6 @@ public function columnTypes() $this->primaryKey()->comment('test comment'), [ 'mysql' => "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'test comment'", - 'sqlsrv' => 'int IDENTITY PRIMARY KEY', ], [ 'sqlsrv' => 'pk', @@ -907,10 +889,9 @@ public function columnTypes() ], [ Schema::TYPE_PK . ' FIRST', - $this->primaryKey()->first(), + in_array($this->driverName, ['mysql'], true) ? $this->primaryKey()->first() : $this->primaryKey(), [ 'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST', - 'sqlsrv' => 'int IDENTITY PRIMARY KEY', ], [ 'oci' => 'NUMBER(10) NOT NULL PRIMARY KEY', @@ -919,10 +900,9 @@ public function columnTypes() ], [ Schema::TYPE_INTEGER . ' FIRST', - $this->integer()->first(), + in_array($this->driverName, ['mysql'], true) ? $this->integer()->first() : $this->integer(), [ 'mysql' => 'int(11) FIRST', - 'sqlsrv' => 'int', ], [ 'oci' => 'NUMBER(10)', @@ -932,10 +912,9 @@ public function columnTypes() ], [ Schema::TYPE_STRING . ' FIRST', - $this->string()->first(), + in_array($this->driverName, ['mysql'], true) ? $this->string()->first() : $this->string(), [ 'mysql' => 'varchar(255) FIRST', - 'sqlsrv' => 'nvarchar(255)', ], [ 'oci' => 'VARCHAR2(255)', @@ -944,10 +923,10 @@ public function columnTypes() ], [ Schema::TYPE_INTEGER . ' NOT NULL FIRST', - $this->integer()->append('NOT NULL')->first(), + in_array($this->driverName, ['mysql'], true) + ? $this->integer()->append('NOT NULL')->first() : $this->integer()->append('NOT NULL'), [ 'mysql' => 'int(11) NOT NULL FIRST', - 'sqlsrv' => 'int NOT NULL', ], [ 'oci' => 'NUMBER(10) NOT NULL', @@ -956,10 +935,10 @@ public function columnTypes() ], [ Schema::TYPE_STRING . ' NOT NULL FIRST', - $this->string()->append('NOT NULL')->first(), + in_array($this->driverName, ['mysql'], true) + ? $this->string()->append('NOT NULL')->first() : $this->string()->append('NOT NULL'), [ 'mysql' => 'varchar(255) NOT NULL FIRST', - 'sqlsrv' => 'nvarchar(255) NOT NULL', ], [ 'oci' => 'VARCHAR2(255) NOT NULL', @@ -1012,9 +991,7 @@ public function testCreateTableColumnTypes() foreach ($this->columnTypes() as $item) { list($column, $builder, $expected) = $item; if (!(strncmp($column, Schema::TYPE_PK, 2) === 0 || - strncmp($column, Schema::TYPE_UPK, 3) === 0 || strncmp($column, Schema::TYPE_BIGPK, 5) === 0 || - strncmp($column, Schema::TYPE_UBIGPK, 6) === 0 || strncmp(substr($column, -5), 'FIRST', 5) === 0 )) { $columns['col' . ++$i] = str_replace('CHECK (value', 'CHECK ([[col' . $i . ']]', $column); diff --git a/tests/framework/db/command/types/AbstractExecuteColumnTypesTest.php b/tests/framework/db/command/types/AbstractExecuteColumnTypesTest.php new file mode 100644 index 00000000..ed5d41fb --- /dev/null +++ b/tests/framework/db/command/types/AbstractExecuteColumnTypesTest.php @@ -0,0 +1,73 @@ +db->close(); + $this->db = null; + + parent::tearDown(); + } + + public function executeColumnTypes( + Closure|string $abstractColumn, + string $expectedColumnSchemaType, + bool|null $isPrimaryKey, + string $expectedColumnType, + int|string $expectedLastInsertID, + ): void { + if (is_callable($abstractColumn)) { + $abstractColumn = $abstractColumn($this->db->schema); + } + + $columns = [ + 'id' => $abstractColumn, + 'name' => $this->db->schema->createColumnSchemaBuilder(Schema::TYPE_STRING) + ]; + + // Ensure column schema type + $this->assertSame($expectedColumnSchemaType, $this->db->queryBuilder->getColumnType($columns['id'])); + + $result = TableGenerator::ensureTable($this->db, $this->table, $columns); + + // Ensure table was created + $this->assertSame(0, $result); + + $column = $this->db->getTableSchema($this->table)->getColumn('id'); + + // Ensure column was created + $this->assertSame($isPrimaryKey, $column->isPrimaryKey); + $this->assertTrue($column->autoIncrement); + $this->assertSame($expectedColumnType, $column->type); + $this->assertFalse($column->allowNull); + + $result = $this->db->createCommand()->batchInsert($this->table, ['name'], [['test1'], ['test2']])->execute(); + + // Ensure data was inserted + $this->assertSame(2, $result); + + // Ensure last insert ID. + // MySQL not return last insert ID for batch insert. + $lastInsertID = match ($this->db->getDriverName()) { + 'mysql' => $this->db->createCommand("SELECT MAX(id) FROM {$this->table}")->queryScalar(), + default => $this->db->getLastInsertID(), + }; + + $this->assertSame($expectedLastInsertID, $lastInsertID); + + TableGenerator::ensureNoTable($this->db, $this->table); + } +} diff --git a/tests/framework/db/mssql/ColumnSchemaBuilderTest.php b/tests/framework/db/mssql/ColumnSchemaBuilderTest.php index 783d4aa1..0f3c32ed 100644 --- a/tests/framework/db/mssql/ColumnSchemaBuilderTest.php +++ b/tests/framework/db/mssql/ColumnSchemaBuilderTest.php @@ -1,30 +1,24 @@ getConnection()); + return new ColumnSchemaBuilder($this->getConnection(), $type, $length); } } diff --git a/tests/framework/db/mssql/command/types/AutoIncrementTest.php b/tests/framework/db/mssql/command/types/AutoIncrementTest.php new file mode 100644 index 00000000..14ff52a1 --- /dev/null +++ b/tests/framework/db/mssql/command/types/AutoIncrementTest.php @@ -0,0 +1,44 @@ +db = MssqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mssql\provider\types\AutoIncrementProvider::command + */ + public function testExecute( + Closure|string $abstractColumn, + string $expectedColumnSchemaType, + bool|null $isPrimaryKey, + string $expectedColumnType, + int|string $expectedLastInsertID, + ): void { + parent::executeColumnTypes( + $abstractColumn, + $expectedColumnSchemaType, + $isPrimaryKey, + $expectedColumnType, + $expectedLastInsertID, + ); + } +} diff --git a/tests/framework/db/mssql/command/types/BigAutoIncrementTest.php b/tests/framework/db/mssql/command/types/BigAutoIncrementTest.php new file mode 100644 index 00000000..1b979e08 --- /dev/null +++ b/tests/framework/db/mssql/command/types/BigAutoIncrementTest.php @@ -0,0 +1,44 @@ +db = MssqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mssql\provider\types\BigAutoIncrementProvider::command + */ + public function testExecute( + Closure|string $abstractColumn, + string $expectedColumnSchemaType, + bool|null $isPrimaryKey, + string $expectedColumnType, + int|string $expectedLastInsertID, + ): void { + parent::executeColumnTypes( + $abstractColumn, + $expectedColumnSchemaType, + $isPrimaryKey, + $expectedColumnType, + $expectedLastInsertID, + ); + } +} diff --git a/tests/framework/db/mssql/command/types/BigPrimaryKeyTest.php b/tests/framework/db/mssql/command/types/BigPrimaryKeyTest.php new file mode 100644 index 00000000..ff11e3b1 --- /dev/null +++ b/tests/framework/db/mssql/command/types/BigPrimaryKeyTest.php @@ -0,0 +1,44 @@ +db = MssqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mssql\provider\types\BigPrimaryKeyProvider::command + */ + public function testExecute( + Closure|string $abstractColumn, + string $expectedColumnSchemaType, + bool|null $isPrimaryKey, + string $expectedColumnType, + int|string $expectedLastInsertID, + ): void { + parent::executeColumnTypes( + $abstractColumn, + $expectedColumnSchemaType, + $isPrimaryKey, + $expectedColumnType, + $expectedLastInsertID, + ); + } +} diff --git a/tests/framework/db/mssql/command/types/PrimaryKeyTest.php b/tests/framework/db/mssql/command/types/PrimaryKeyTest.php new file mode 100644 index 00000000..321d14bf --- /dev/null +++ b/tests/framework/db/mssql/command/types/PrimaryKeyTest.php @@ -0,0 +1,44 @@ +db = MssqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mssql\provider\types\PrimaryKeyProvider::command + */ + public function testExecute( + Closure|string $abstractColumn, + string $expectedColumnSchemaType, + bool|null $isPrimaryKey, + string $expectedColumnType, + int|string $expectedLastInsertID, + ): void { + parent::executeColumnTypes( + $abstractColumn, + $expectedColumnSchemaType, + $isPrimaryKey, + $expectedColumnType, + $expectedLastInsertID, + ); + } +} diff --git a/tests/framework/db/mssql/provider/types/AutoIncrementProvider.php b/tests/framework/db/mssql/provider/types/AutoIncrementProvider.php new file mode 100644 index 00000000..dd9a0a81 --- /dev/null +++ b/tests/framework/db/mssql/provider/types/AutoIncrementProvider.php @@ -0,0 +1,188 @@ + $schema->createColumnSchemaBuilder(Schema::TYPE_AUTO), + 'int IDENTITY', + null, + 'integer', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_AUTO, [1]), + 'int IDENTITY', + null, + 'integer', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_AUTO, [0, 0]), + 'int IDENTITY(0,1)', + null, + 'integer', + '1', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_AUTO, [1, 1]), + 'int IDENTITY(1,1)', + null, + 'integer', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_AUTO, [2, 3]), + 'int IDENTITY(2,3)', + null, + 'integer', + '5', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_AUTO, [-10, 1]), + 'int IDENTITY(-10,1)', + null, + 'integer', + '-9', + ], + // builder shortcut + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->autoIncrement(), + 'int IDENTITY', + null, + 'integer', + '2', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->autoIncrement(1), + 'int IDENTITY', + null, + 'integer', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->autoIncrement(0, 0), + 'int IDENTITY(0,1)', + null, + 'integer', + '1', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->autoIncrement(1, 1), + 'int IDENTITY(1,1)', + null, + 'integer', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->autoIncrement(2, 3), + 'int IDENTITY(2,3)', + null, + 'integer', + '5', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->autoIncrement(-10, 1), + 'int IDENTITY(-10,1)', + null, + 'integer', + '-9', + ], + ]; + } + + public static function queryBuilder(): array + { + $expected = [ + 'auto' => [ + 1 => 'auto', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->autoIncrement(), + 3 => 'int IDENTITY', + ], + 'auto(1)' => [ + 1 => 'auto', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->autoIncrement(1), + 3 => 'int IDENTITY', + ], + 'auto(0,0)' => [ + 1 => 'auto(0,1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->autoIncrement(0, 0), + 3 => 'int IDENTITY(0,1)', + ], + 'auto(1,1)' => [ + 1 => 'auto(1,1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->autoIncrement(1, 1), + 3 => 'int IDENTITY(1,1)', + ], + 'auto(2,3)' => [ + 1 => 'auto(2,3)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->autoIncrement(2, 3), + 3 => 'int IDENTITY(2,3)', + ], + 'auto(-10,1)' => [ + Schema::TYPE_AUTO . '(-10,1)', + 'auto(-10,1)', + static fn (ColumnSchemaBuilder $builder) => $builder->autoIncrement(-10, 1), + 'int IDENTITY(-10,1)', + ], + ]; + + $types = parent::queryBuilder(); + + return TestHelper::addExpected($expected, $types); + } +} diff --git a/tests/framework/db/mssql/provider/types/BigAutoIncrementProvider.php b/tests/framework/db/mssql/provider/types/BigAutoIncrementProvider.php new file mode 100644 index 00000000..cef157a5 --- /dev/null +++ b/tests/framework/db/mssql/provider/types/BigAutoIncrementProvider.php @@ -0,0 +1,188 @@ + $schema->createColumnSchemaBuilder(Schema::TYPE_BIGAUTO), + 'bigint IDENTITY', + null, + 'bigint', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGAUTO, [1]), + 'bigint IDENTITY', + null, + 'bigint', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGAUTO, [0, 0]), + 'bigint IDENTITY(0,1)', + null, + 'bigint', + '1', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGAUTO, [1, 1]), + 'bigint IDENTITY(1,1)', + null, + 'bigint', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGAUTO, [2, 3]), + 'bigint IDENTITY(2,3)', + null, + 'bigint', + '5', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGAUTO, [-10, 1]), + 'bigint IDENTITY(-10,1)', + null, + 'bigint', + '-9', + ], + // builder generator + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigAutoIncrement(), + 'bigint IDENTITY', + null, + 'bigint', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigAutoIncrement(1), + 'bigint IDENTITY', + null, + 'bigint', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigAutoIncrement(0, 0), + 'bigint IDENTITY(0,1)', + null, + 'bigint', + '1', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigAutoIncrement(1, 1), + 'bigint IDENTITY(1,1)', + null, + 'bigint', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigAutoIncrement(2, 3), + 'bigint IDENTITY(2,3)', + null, + 'bigint', + '5', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigAutoIncrement(-10, 1), + 'bigint IDENTITY(-10,1)', + null, + 'bigint', + '-9', + ], + ]; + } + + public static function queryBuilder(): array + { + $expected = [ + 'bigauto' => [ + 1 => 'bigauto', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigAutoIncrement(), + 3 => 'bigint IDENTITY', + ], + 'bigauto(1)' => [ + 1 => 'bigauto', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigAutoIncrement(1), + 3 => 'bigint IDENTITY', + ], + 'bigauto(0,0)' => [ + 1 => 'bigauto(0,1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigAutoIncrement(0, 0), + 3 => 'bigint IDENTITY(0,1)', + ], + 'bigauto(1,1)' => [ + 1 => 'bigauto(1,1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigAutoIncrement(1, 1), + 3 => 'bigint IDENTITY(1,1)', + ], + 'bigauto(2,3)' => [ + 1 => 'bigauto(2,3)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigAutoIncrement(2, 3), + 3 => 'bigint IDENTITY(2,3)', + ], + 'bigauto(-10,1)' => [ + Schema::TYPE_BIGAUTO . '(-10,1)', + 'bigauto(-10,1)', + static fn (ColumnSchemaBuilder $builder) => $builder->bigAutoIncrement(-10, 1), + 'bigint IDENTITY(-10,1)', + ], + ]; + + $types = parent::queryBuilder(); + + return TestHelper::addExpected($expected, $types); + } +} diff --git a/tests/framework/db/mssql/provider/types/BigPrimaryKeyProvider.php b/tests/framework/db/mssql/provider/types/BigPrimaryKeyProvider.php new file mode 100644 index 00000000..6cec51d7 --- /dev/null +++ b/tests/framework/db/mssql/provider/types/BigPrimaryKeyProvider.php @@ -0,0 +1,188 @@ + $schema->createColumnSchemaBuilder(Schema::TYPE_BIGPK), + 'bigint IDENTITY PRIMARY KEY', + true, + 'bigint', + '2', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGPK, [1]), + 'bigint IDENTITY PRIMARY KEY', + true, + 'bigint', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGPK, [0, 0]), + 'bigint IDENTITY(0,1) PRIMARY KEY', + true, + 'bigint', + '1', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGPK, [1, 1]), + 'bigint IDENTITY(1,1) PRIMARY KEY', + true, + 'bigint', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGPK, [2, 3]), + 'bigint IDENTITY(2,3) PRIMARY KEY', + true, + 'bigint', + '5', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGPK, [-10, 1]), + 'bigint IDENTITY(-10,1) PRIMARY KEY', + true, + 'bigint', + '-9', + ], + // builder shortcut + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigPrimaryKey(), + 'bigint IDENTITY PRIMARY KEY', + true, + 'bigint', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigPrimaryKey(1), + 'bigint IDENTITY PRIMARY KEY', + true, + 'bigint', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigPrimaryKey(0, 0), + 'bigint IDENTITY(0,1) PRIMARY KEY', + true, + 'bigint', + '1', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigPrimaryKey(1, 1), + 'bigint IDENTITY(1,1) PRIMARY KEY', + true, + 'bigint', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigPrimaryKey(2, 3), + 'bigint IDENTITY(2,3) PRIMARY KEY', + true, + 'bigint', + '5', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigPrimaryKey(-10, 1), + 'bigint IDENTITY(-10,1) PRIMARY KEY', + true, + 'bigint', + '-9', + ], + ]; + } + + public static function queryBuilder(): array + { + $expected = [ + 'bigpk' => [ + 1 => 'bigpk', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigPrimaryKey(), + 3 => 'bigint IDENTITY PRIMARY KEY', + ], + 'bigpk(1)' => [ + 1 => 'bigpk', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigPrimaryKey(1), + 3 => 'bigint IDENTITY PRIMARY KEY', + ], + 'bigpk(0,0)' => [ + 1 => 'bigpk(0,1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigPrimaryKey(0, 0), + 3 => 'bigint IDENTITY(0,1) PRIMARY KEY', + ], + 'bigpk(1,1)' => [ + 1 => 'bigpk(1,1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigPrimaryKey(1, 1), + 3 => 'bigint IDENTITY(1,1) PRIMARY KEY', + ], + 'bigpk(2,3)' => [ + 1 => 'bigpk(2,3)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigPrimaryKey(2, 3), + 3 => 'bigint IDENTITY(2,3) PRIMARY KEY', + ], + 'bigpk(-10,1)' => [ + Schema::TYPE_BIGPK . '(-10,1)', + 'bigpk(-10,1)', + static fn (ColumnSchemaBuilder $builder) => $builder->bigPrimaryKey(-10, 1), + 'bigint IDENTITY(-10,1) PRIMARY KEY', + ], + ]; + + $types = parent::queryBuilder(); + + return TestHelper::addExpected($expected, $types); + } +} diff --git a/tests/framework/db/mssql/provider/types/PrimaryKeyProvider.php b/tests/framework/db/mssql/provider/types/PrimaryKeyProvider.php new file mode 100644 index 00000000..63fafbfa --- /dev/null +++ b/tests/framework/db/mssql/provider/types/PrimaryKeyProvider.php @@ -0,0 +1,188 @@ + $schema->createColumnSchemaBuilder(Schema::TYPE_PK), + 'int IDENTITY PRIMARY KEY', + true, + 'integer', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_PK, [1]), + 'int IDENTITY PRIMARY KEY', + true, + 'integer', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_PK, [0, 0]), + 'int IDENTITY(0,1) PRIMARY KEY', + true, + 'integer', + '1', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_PK, [1, 1]), + 'int IDENTITY(1,1) PRIMARY KEY', + true, + 'integer', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_PK, [2, 3]), + 'int IDENTITY(2,3) PRIMARY KEY', + true, + 'integer', + '5', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_PK, [-10, 1]), + 'int IDENTITY(-10,1) PRIMARY KEY', + true, + 'integer', + '-9', + ], + // builder shortcut + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->primaryKey(), + 'int IDENTITY PRIMARY KEY', + true, + 'integer', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->primaryKey(1), + 'int IDENTITY PRIMARY KEY', + true, + 'integer', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->primaryKey(0, 0), + 'int IDENTITY(0,1) PRIMARY KEY', + true, + 'integer', + '1', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->primaryKey(1, 1), + 'int IDENTITY(1,1) PRIMARY KEY', + true, + 'integer', + '2', + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->primaryKey(2, 3), + 'int IDENTITY(2,3) PRIMARY KEY', + true, + 'integer', + '5', + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->primaryKey(-10, 1), + 'int IDENTITY(-10,1) PRIMARY KEY', + true, + 'integer', + '-9', + ], + ]; + } + + public static function queryBuilder(): array + { + $expected = [ + 'pk' => [ + 1 => 'pk', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->primaryKey(), + 3 => 'int IDENTITY PRIMARY KEY', + ], + 'pk(1)' => [ + 1 => 'pk', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->primaryKey(1), + 3 => 'int IDENTITY PRIMARY KEY', + ], + 'pk(0,0)' => [ + 1 => 'pk(0,1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->primaryKey(0, 0), + 3 => 'int IDENTITY(0,1) PRIMARY KEY', + ], + 'pk(1,1)' => [ + 1 => 'pk(1,1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->primaryKey(1, 1), + 3 => 'int IDENTITY(1,1) PRIMARY KEY', + ], + 'pk(2,3)' => [ + 1 => 'pk(2,3)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->primaryKey(2, 3), + 3 => 'int IDENTITY(2,3) PRIMARY KEY', + ], + 'pk(-10,1)' => [ + Schema::TYPE_PK . '(-10,1)', + 'pk(-10,1)', + static fn (ColumnSchemaBuilder $builder) => $builder->primaryKey(-10, 1), + 'int IDENTITY(-10,1) PRIMARY KEY', + ], + ]; + + $types = parent::queryBuilder(); + + return TestHelper::addExpected($expected, $types); + } +} diff --git a/tests/framework/db/mssql/querybuilder/types/AutoIncrementTest.php b/tests/framework/db/mssql/querybuilder/types/AutoIncrementTest.php new file mode 100644 index 00000000..8dd3ac14 --- /dev/null +++ b/tests/framework/db/mssql/querybuilder/types/AutoIncrementTest.php @@ -0,0 +1,37 @@ +db = MssqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mssql\provider\types\AutoIncrementProvider::queryBuilder + */ + public function testGenerateColumnType( + string $column, + string $expectedColumn, + Closure $builder, + string $expectedBuilder, + ): void { + $this->getColumnType($column, $expectedColumn, $builder, $expectedBuilder); + } +} diff --git a/tests/framework/db/mssql/querybuilder/types/BigAutoIncrementTest.php b/tests/framework/db/mssql/querybuilder/types/BigAutoIncrementTest.php new file mode 100644 index 00000000..282e0168 --- /dev/null +++ b/tests/framework/db/mssql/querybuilder/types/BigAutoIncrementTest.php @@ -0,0 +1,37 @@ +db = MssqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mssql\provider\types\BigAutoIncrementProvider::queryBuilder + */ + public function testGenerateColumnType( + string $column, + string $expectedColumn, + Closure $builder, + string $expectedBuilder, + ): void { + $this->getColumnType($column, $expectedColumn, $builder, $expectedBuilder); + } +} diff --git a/tests/framework/db/mssql/querybuilder/types/BigPrimaryKeyTest.php b/tests/framework/db/mssql/querybuilder/types/BigPrimaryKeyTest.php new file mode 100644 index 00000000..ec81b019 --- /dev/null +++ b/tests/framework/db/mssql/querybuilder/types/BigPrimaryKeyTest.php @@ -0,0 +1,37 @@ +db = MssqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mssql\provider\types\BigPrimaryKeyProvider::queryBuilder + */ + public function testGenerateColumnType( + string $column, + string $expectedColumn, + Closure $builder, + string $expectedBuilder, + ): void { + $this->getColumnType($column, $expectedColumn, $builder, $expectedBuilder); + } +} diff --git a/tests/framework/db/mssql/querybuilder/types/PrimaryKeyTest.php b/tests/framework/db/mssql/querybuilder/types/PrimaryKeyTest.php new file mode 100644 index 00000000..712694a1 --- /dev/null +++ b/tests/framework/db/mssql/querybuilder/types/PrimaryKeyTest.php @@ -0,0 +1,37 @@ +db = MssqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mssql\provider\types\PrimaryKeyProvider::queryBuilder + */ + public function testGenerateColumnType( + string $column, + string $expectedColumn, + Closure $builder, + string $expectedBuilder, + ): void { + $this->getColumnType($column, $expectedColumn, $builder, $expectedBuilder); + } +} diff --git a/tests/framework/db/mysql/ColumnSchemaBuilderTest.php b/tests/framework/db/mysql/ColumnSchemaBuilderTest.php index e930deef..e5c61356 100644 --- a/tests/framework/db/mysql/ColumnSchemaBuilderTest.php +++ b/tests/framework/db/mysql/ColumnSchemaBuilderTest.php @@ -1,9 +1,6 @@ getConnection()); + return new ColumnSchemaBuilder($this->getConnection(), $type, $length); } /** diff --git a/tests/framework/db/mysql/command/types/AutoIncrementTest.php b/tests/framework/db/mysql/command/types/AutoIncrementTest.php new file mode 100644 index 00000000..15a28dfd --- /dev/null +++ b/tests/framework/db/mysql/command/types/AutoIncrementTest.php @@ -0,0 +1,44 @@ +db = MysqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mysql\provider\types\AutoIncrementProvider::command + */ + public function testExecute( + Closure|string $abstractColumn, + string $expectedColumnSchemaType, + bool|null $isPrimaryKey, + string $expectedColumnType, + int|string $expectedLastInsertID, + ): void { + parent::executeColumnTypes( + $abstractColumn, + $expectedColumnSchemaType, + $isPrimaryKey, + $expectedColumnType, + $expectedLastInsertID, + ); + } +} diff --git a/tests/framework/db/mysql/command/types/BigAutoIncrementTest.php b/tests/framework/db/mysql/command/types/BigAutoIncrementTest.php new file mode 100644 index 00000000..08beb854 --- /dev/null +++ b/tests/framework/db/mysql/command/types/BigAutoIncrementTest.php @@ -0,0 +1,44 @@ +db = MysqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mysql\provider\types\BigAutoIncrementProvider::command + */ + public function testExecute( + Closure|string $abstractColumn, + string $expectedColumnSchemaType, + bool|null $isPrimaryKey, + string $expectedColumnType, + int|string $expectedLastInsertID, + ): void { + parent::executeColumnTypes( + $abstractColumn, + $expectedColumnSchemaType, + $isPrimaryKey, + $expectedColumnType, + $expectedLastInsertID, + ); + } +} diff --git a/tests/framework/db/mysql/command/types/BigPrimaryKeyTest.php b/tests/framework/db/mysql/command/types/BigPrimaryKeyTest.php new file mode 100644 index 00000000..d24ac1e2 --- /dev/null +++ b/tests/framework/db/mysql/command/types/BigPrimaryKeyTest.php @@ -0,0 +1,44 @@ +db = MysqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mysql\provider\types\BigPrimaryKeyProvider::command + */ + public function testExecute( + Closure|string $abstractColumn, + string $expectedColumnSchemaType, + bool|null $isPrimaryKey, + string $expectedColumnType, + int|string $expectedLastInsertID, + ): void { + parent::executeColumnTypes( + $abstractColumn, + $expectedColumnSchemaType, + $isPrimaryKey, + $expectedColumnType, + $expectedLastInsertID, + ); + } +} diff --git a/tests/framework/db/mysql/command/types/PrimaryKeyTest.php b/tests/framework/db/mysql/command/types/PrimaryKeyTest.php new file mode 100644 index 00000000..af9c7d91 --- /dev/null +++ b/tests/framework/db/mysql/command/types/PrimaryKeyTest.php @@ -0,0 +1,44 @@ +db = MysqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mysql\provider\types\PrimaryKeyProvider::command + */ + public function testExecute( + Closure|string $abstractColumn, + string $expectedColumnSchemaType, + bool|null $isPrimaryKey, + string $expectedColumnType, + int|string $expectedLastInsertID, + ): void { + parent::executeColumnTypes( + $abstractColumn, + $expectedColumnSchemaType, + $isPrimaryKey, + $expectedColumnType, + $expectedLastInsertID, + ); + } +} diff --git a/tests/framework/db/mysql/provider/types/AutoIncrementProvider.php b/tests/framework/db/mysql/provider/types/AutoIncrementProvider.php new file mode 100644 index 00000000..536ab022 --- /dev/null +++ b/tests/framework/db/mysql/provider/types/AutoIncrementProvider.php @@ -0,0 +1,140 @@ + $schema->createColumnSchemaBuilder(Schema::TYPE_AUTO), + 'int(11) AUTO_INCREMENT', + true, + 'integer', + 2, + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_AUTO, 1), + 'int(1) AUTO_INCREMENT', + true, + 'integer', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(\yii\db\mysql\Schema::TYPE_UAUTO), + 'int(10) UNSIGNED AUTO_INCREMENT', + true, + 'integer', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(\yii\db\mysql\Schema::TYPE_UAUTO, 1), + 'int(1) UNSIGNED AUTO_INCREMENT', + true, + 'integer', + 2, + ], + // builder shortcut + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->autoIncrement(), + 'int(11) AUTO_INCREMENT', + true, + 'integer', + 2, + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->autoIncrement(1), + 'int(1) AUTO_INCREMENT', + true, + 'integer', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->autoIncrement()->unsigned(), + 'int(10) UNSIGNED AUTO_INCREMENT', + true, + 'integer', + 2, + ], + [ + 'id' => static fn (Schema $schema) => $schema + ->createColumnSchemaBuilder() + ->autoIncrement(1) + ->unsigned(), + 'int(1) UNSIGNED AUTO_INCREMENT', + true, + 'integer', + 2, + ], + ]; + } + + public static function queryBuilder(): array + { + $expected = [ + 'auto' => [ + 1 => 'auto', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->autoIncrement(), + 3 => 'int(11) AUTO_INCREMENT', + ], + 'auto(1)' => [ + 1 => 'auto(1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->autoIncrement(1), + 3 => 'int(1) AUTO_INCREMENT', + ], + [ + \yii\db\mysql\Schema::TYPE_UAUTO, + 'uauto', + static fn (ColumnSchemaBuilder $builder) => $builder->autoIncrement()->unsigned(), + 'int(10) UNSIGNED AUTO_INCREMENT', + ], + [ + \yii\db\mysql\Schema::TYPE_UAUTO . '(1)', + 'uauto(1)', + static fn (ColumnSchemaBuilder $builder) => $builder->autoIncrement(1)->unsigned(), + 'int(1) UNSIGNED AUTO_INCREMENT', + ], + ]; + + $types = parent::queryBuilder(); + + return TestHelper::addExpected($expected, $types); + } +} diff --git a/tests/framework/db/mysql/provider/types/BigAutoIncrementProvider.php b/tests/framework/db/mysql/provider/types/BigAutoIncrementProvider.php new file mode 100644 index 00000000..b36682b5 --- /dev/null +++ b/tests/framework/db/mysql/provider/types/BigAutoIncrementProvider.php @@ -0,0 +1,144 @@ + $schema->createColumnSchemaBuilder(Schema::TYPE_BIGAUTO), + 'bigint(20) AUTO_INCREMENT', + true, + 'bigint', + 2, + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGAUTO, 1), + 'bigint(1) AUTO_INCREMENT', + true, + 'bigint', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(\yii\db\mysql\Schema::TYPE_UBIGAUTO), + 'bigint(20) UNSIGNED AUTO_INCREMENT', + true, + 'bigint', + 2, + ], + [ + 'id' => static fn (Schema $schema) => $schema + ->createColumnSchemaBuilder(\yii\db\mysql\Schema::TYPE_UBIGAUTO, 1), + 'bigint(1) UNSIGNED AUTO_INCREMENT', + true, + 'bigint', + 2, + ], + // builder shortcut + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigAutoIncrement(), + 'bigint(20) AUTO_INCREMENT', + true, + 'bigint', + 2, + ], + [ + 'id' => static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigAutoIncrement(1), + 'bigint(1) AUTO_INCREMENT', + true, + 'bigint', + 2, + ], + [ + 'id' => static fn (Schema $schema) => $schema + ->createColumnSchemaBuilder() + ->bigAutoIncrement() + ->unsigned(), + 'bigint(20) UNSIGNED AUTO_INCREMENT', + true, + 'bigint', + 2, + ], + [ + 'id' => static fn (Schema $schema) => $schema + ->createColumnSchemaBuilder() + ->bigAutoIncrement(1) + ->unsigned(), + 'bigint(1) UNSIGNED AUTO_INCREMENT', + true, + 'bigint', + 2, + ], + ]; + } + + public static function queryBuilder(): array + { + $expected = [ + 'bigauto' => [ + 1 => 'bigauto', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigAutoIncrement(), + 3 => 'bigint(20) AUTO_INCREMENT', + ], + 'bigauto(1)' => [ + 1 => 'bigauto(1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigAutoIncrement(1), + 3 => 'bigint(1) AUTO_INCREMENT', + ], + [ + \yii\db\mysql\Schema::TYPE_UBIGAUTO, + 'ubigauto', + static fn (ColumnSchemaBuilder $builder) => $builder->bigAutoIncrement()->unsigned(), + 'bigint(20) UNSIGNED AUTO_INCREMENT', + ], + [ + \yii\db\mysql\Schema::TYPE_UBIGAUTO . '(1)', + 'ubigauto(1)', + static fn (ColumnSchemaBuilder $builder) => $builder->bigAutoIncrement(1)->unsigned(), + 'bigint(1) UNSIGNED AUTO_INCREMENT', + ], + ]; + + $types = parent::queryBuilder(); + + return TestHelper::addExpected($expected, $types); + } +} diff --git a/tests/framework/db/mysql/provider/types/BigPrimaryKeyProvider.php b/tests/framework/db/mysql/provider/types/BigPrimaryKeyProvider.php new file mode 100644 index 00000000..07196343 --- /dev/null +++ b/tests/framework/db/mysql/provider/types/BigPrimaryKeyProvider.php @@ -0,0 +1,137 @@ + $schema->createColumnSchemaBuilder(Schema::TYPE_BIGPK), + 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'bigint', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_BIGPK, 1), + 'bigint(1) NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'bigint', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(\yii\db\mysql\Schema::TYPE_UBIGPK), + 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'bigint', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(\yii\db\mysql\Schema::TYPE_UBIGPK, 1), + 'bigint(1) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'bigint', + 2, + ], + // builder shortcut + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigPrimaryKey(), + 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'bigint', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigPrimaryKey(1), + 'bigint(1) NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'bigint', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigPrimaryKey()->unsigned(), + 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'bigint', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->bigPrimaryKey(1)->unsigned(), + 'bigint(1) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'bigint', + 2, + ], + ]; + } + + public static function queryBuilder(): array + { + $expected = [ + 'bigpk' => [ + 1 => 'bigpk', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigPrimaryKey(), + 3 => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY', + ], + 'bigpk(1)' => [ + 1 => 'bigpk(1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->bigPrimaryKey(1), + 3 => 'bigint(1) NOT NULL AUTO_INCREMENT PRIMARY KEY', + ], + [ + \yii\db\mysql\Schema::TYPE_UBIGPK, + 'ubigpk', + static fn (ColumnSchemaBuilder $builder) => $builder->bigPrimaryKey()->unsigned(), + 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + ], + [ + \yii\db\mysql\Schema::TYPE_UBIGPK . '(1)', + 'ubigpk(1)', + static fn (ColumnSchemaBuilder $builder) => $builder->bigPrimaryKey(1)->unsigned(), + 'bigint(1) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + ], + ]; + + $types = parent::queryBuilder(); + + return TestHelper::addExpected($expected, $types); + } +} diff --git a/tests/framework/db/mysql/provider/types/PrimaryKeyProvider.php b/tests/framework/db/mysql/provider/types/PrimaryKeyProvider.php new file mode 100644 index 00000000..aa0c2ede --- /dev/null +++ b/tests/framework/db/mysql/provider/types/PrimaryKeyProvider.php @@ -0,0 +1,137 @@ + $schema->createColumnSchemaBuilder(Schema::TYPE_PK), + 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'integer', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(Schema::TYPE_PK, 1), + 'int(1) NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'integer', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(\yii\db\mysql\Schema::TYPE_UPK), + 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'integer', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder(\yii\db\mysql\Schema::TYPE_UPK, 1), + 'int(1) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'integer', + 2, + ], + // builder shorcut + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->primaryKey(), + 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'integer', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->primaryKey(1), + 'int(1) NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'integer', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->primaryKey()->unsigned(), + 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'integer', + 2, + ], + [ + static fn (Schema $schema) => $schema->createColumnSchemaBuilder()->primaryKey(1)->unsigned(), + 'int(1) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + true, + 'integer', + 2, + ], + ]; + } + + public static function queryBuilder(): array + { + $expected = [ + 'pk' => [ + 1 => 'pk', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->primaryKey(), + 3 => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', + ], + 'pk(1)' => [ + 1 => 'pk(1)', + 2 => static fn (ColumnSchemaBuilder $builder) => $builder->primaryKey(1), + 3 => 'int(1) NOT NULL AUTO_INCREMENT PRIMARY KEY', + ], + [ + \yii\db\mysql\Schema::TYPE_UPK, + 'upk', + static fn (ColumnSchemaBuilder $builder) => $builder->primaryKey()->unsigned(), + 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + ], + [ + \yii\db\mysql\Schema::TYPE_UPK . '(1)', + 'upk(1)', + static fn (ColumnSchemaBuilder $builder) => $builder->primaryKey(1)->unsigned(), + 'int(1) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', + ], + ]; + + $types = parent::queryBuilder(); + + return TestHelper::addExpected($expected, $types); + } +} diff --git a/tests/framework/db/mysql/querybuilder/types/AutoIncrementTest.php b/tests/framework/db/mysql/querybuilder/types/AutoIncrementTest.php new file mode 100644 index 00000000..3680005c --- /dev/null +++ b/tests/framework/db/mysql/querybuilder/types/AutoIncrementTest.php @@ -0,0 +1,37 @@ +db = MysqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mysql\provider\types\AutoIncrementProvider::queryBuilder + */ + public function testGenerateColumnType( + string $column, + string $expectedColumn, + Closure $builder, + string $expectedBuilder, + ): void { + $this->getColumnType($column, $expectedColumn, $builder, $expectedBuilder); + } +} diff --git a/tests/framework/db/mysql/querybuilder/types/BigAutoIncrementTest.php b/tests/framework/db/mysql/querybuilder/types/BigAutoIncrementTest.php new file mode 100644 index 00000000..d9503f06 --- /dev/null +++ b/tests/framework/db/mysql/querybuilder/types/BigAutoIncrementTest.php @@ -0,0 +1,37 @@ +db = MysqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mysql\provider\types\BigAutoIncrementProvider::queryBuilder + */ + public function testGenerateColumnType( + string $column, + string $expectedColumn, + Closure $builder, + string $expectedBuilder, + ): void { + $this->getColumnType($column, $expectedColumn, $builder, $expectedBuilder); + } +} diff --git a/tests/framework/db/mysql/querybuilder/types/BigPrimaryKeyTest.php b/tests/framework/db/mysql/querybuilder/types/BigPrimaryKeyTest.php new file mode 100644 index 00000000..3e465038 --- /dev/null +++ b/tests/framework/db/mysql/querybuilder/types/BigPrimaryKeyTest.php @@ -0,0 +1,37 @@ +db = MysqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mysql\provider\types\BigPrimaryKeyProvider::queryBuilder + */ + public function testGenerateColumnType( + string $column, + string $expectedColumn, + Closure $builder, + string $expectedBuilder, + ): void { + $this->getColumnType($column, $expectedColumn, $builder, $expectedBuilder); + } +} diff --git a/tests/framework/db/mysql/querybuilder/types/PrimaryKeyTest.php b/tests/framework/db/mysql/querybuilder/types/PrimaryKeyTest.php new file mode 100644 index 00000000..9537b648 --- /dev/null +++ b/tests/framework/db/mysql/querybuilder/types/PrimaryKeyTest.php @@ -0,0 +1,37 @@ +db = MysqlConnection::getConnection(); + } + + /** + * @dataProvider \yiiunit\framework\db\mysql\provider\types\PrimaryKeyProvider::queryBuilder + */ + public function testGenerateColumnType( + string $column, + string $expectedColumn, + Closure $builder, + string $expectedBuilder, + ): void { + $this->getColumnType($column, $expectedColumn, $builder, $expectedBuilder); + } +} diff --git a/tests/framework/db/oci/ColumnSchemaBuilderTest.php b/tests/framework/db/oci/ColumnSchemaBuilderTest.php index 8e0a8e96..35f126c8 100644 --- a/tests/framework/db/oci/ColumnSchemaBuilderTest.php +++ b/tests/framework/db/oci/ColumnSchemaBuilderTest.php @@ -1,9 +1,6 @@ getConnection()); - } - - /** - * @return array - */ - public function typesProvider() + public function getColumnSchemaBuilder($type, $length = null): ColumnSchemaBuilder { - return [ - ['integer UNSIGNED', Schema::TYPE_INTEGER, null, [ - ['unsigned'], - ]], - ['integer(10) UNSIGNED', Schema::TYPE_INTEGER, 10, [ - ['unsigned'], - ]], - ]; + return new ColumnSchemaBuilder($this->getConnection(), $type, $length); } } diff --git a/tests/framework/db/pgsql/ColumnSchemaBuilderTest.php b/tests/framework/db/pgsql/ColumnSchemaBuilderTest.php index 32d4f0eb..c5c0a57b 100644 --- a/tests/framework/db/pgsql/ColumnSchemaBuilderTest.php +++ b/tests/framework/db/pgsql/ColumnSchemaBuilderTest.php @@ -1,30 +1,24 @@ getConnection()); + return new ColumnSchemaBuilder($this->getConnection(), $type, $length); } } diff --git a/tests/framework/db/pgsql/QueryBuilderTest.php b/tests/framework/db/pgsql/QueryBuilderTest.php index 918f3021..eeb7492f 100644 --- a/tests/framework/db/pgsql/QueryBuilderTest.php +++ b/tests/framework/db/pgsql/QueryBuilderTest.php @@ -41,16 +41,6 @@ public function columnTypes() $this->char(6)->check('value LIKE \'test%\''), 'char(6) CHECK (value LIKE \'test%\')', ], - [ - Schema::TYPE_CHAR . '(6)', - $this->char(6)->unsigned(), - 'char(6)', - ], - [ - Schema::TYPE_INTEGER . '(8)', - $this->integer(8)->unsigned(), - 'integer', - ], [ Schema::TYPE_TIMESTAMP . '(4)', $this->timestamp(4), diff --git a/tests/framework/db/provider/types/AbstractAutoIncrementProvider.php b/tests/framework/db/provider/types/AbstractAutoIncrementProvider.php new file mode 100644 index 00000000..d2c355dd --- /dev/null +++ b/tests/framework/db/provider/types/AbstractAutoIncrementProvider.php @@ -0,0 +1,31 @@ + [ + Schema::TYPE_AUTO, + ], + 'auto(1)' => [ + Schema::TYPE_AUTO . '(1)', + ], + 'auto(0,0)' => [ + Schema::TYPE_AUTO . '(0,0)', + ], + 'auto(1,1)' => [ + Schema::TYPE_AUTO . '(1,1)', + ], + 'auto(2,3)' => [ + Schema::TYPE_AUTO . '(2,3)', + ], + ]; + } +} diff --git a/tests/framework/db/provider/types/AbstractBigAutoIncrementProvider.php b/tests/framework/db/provider/types/AbstractBigAutoIncrementProvider.php new file mode 100644 index 00000000..c85eb222 --- /dev/null +++ b/tests/framework/db/provider/types/AbstractBigAutoIncrementProvider.php @@ -0,0 +1,31 @@ + [ + Schema::TYPE_BIGAUTO, + ], + 'bigauto(1)' => [ + Schema::TYPE_BIGAUTO . '(1)', + ], + 'bigauto(0,0)' => [ + Schema::TYPE_BIGAUTO . '(0,0)', + ], + 'bigauto(1,1)' => [ + Schema::TYPE_BIGAUTO . '(1,1)', + ], + 'bigauto(2,3)' => [ + Schema::TYPE_BIGAUTO . '(2,3)', + ], + ]; + } +} diff --git a/tests/framework/db/provider/types/AbstractBigPrimaryKeyProvider.php b/tests/framework/db/provider/types/AbstractBigPrimaryKeyProvider.php new file mode 100644 index 00000000..001f887e --- /dev/null +++ b/tests/framework/db/provider/types/AbstractBigPrimaryKeyProvider.php @@ -0,0 +1,31 @@ + [ + Schema::TYPE_BIGPK, + ], + 'bigpk(1)' => [ + Schema::TYPE_BIGPK . '(1)', + ], + 'bigpk(0,0)' => [ + Schema::TYPE_BIGPK . '(0,0)', + ], + 'bigpk(1,1)' => [ + Schema::TYPE_BIGPK . '(1,1)', + ], + 'bigpk(2,3)' => [ + Schema::TYPE_BIGPK . '(2,3)', + ], + ]; + } +} diff --git a/tests/framework/db/provider/types/AbstractPrimaryKeyProvider.php b/tests/framework/db/provider/types/AbstractPrimaryKeyProvider.php new file mode 100644 index 00000000..f97e35ef --- /dev/null +++ b/tests/framework/db/provider/types/AbstractPrimaryKeyProvider.php @@ -0,0 +1,31 @@ + [ + Schema::TYPE_PK, + ], + 'pk(1)' => [ + Schema::TYPE_PK . '(1)', + ], + 'pk(0,0)' => [ + Schema::TYPE_PK . '(0,0)', + ], + 'pk(1,1)' => [ + Schema::TYPE_PK . '(1,1)', + ], + 'pk(2,3)' => [ + Schema::TYPE_PK . '(2,3)', + ], + ]; + } +} diff --git a/tests/framework/db/querybuilder/types/AbstractColumnType.php b/tests/framework/db/querybuilder/types/AbstractColumnType.php new file mode 100644 index 00000000..4f25e6d8 --- /dev/null +++ b/tests/framework/db/querybuilder/types/AbstractColumnType.php @@ -0,0 +1,49 @@ +db->close(); + $this->db = null; + + parent::tearDown(); + } + + public function getColumnType( + string $column, + string $expectedColumn, + Closure $builder, + string $expectedBuilder, + ): void { + $columnSchemaBuilder = $this->db->schema->createColumnSchemaBuilder(); + $qb = $this->db->getQueryBuilder(); + + $this->assertSame($expectedColumn, $builder($columnSchemaBuilder)->__toString()); + $this->assertSame($expectedBuilder, $qb->getColumnType($column)); + $this->assertSame($expectedBuilder, $qb->getColumnType($builder($columnSchemaBuilder))); + } + + public function getColumnTypeRaw(string $sql, string $column, Closure $builder, string $expectedColumn): void + { + $columnSchemaBuilder = $this->db->schema->createColumnSchemaBuilder(); + $qb = $this->db->getQueryBuilder(); + + if ($expectedColumn === '') { + $expectedColumn = $sql; + } + + $this->assertSame($column, $builder($columnSchemaBuilder)->__toString()); + $this->assertSame($expectedColumn, $qb->getColumnType($sql)); + $this->assertSame($expectedColumn, $qb->getColumnType($builder($columnSchemaBuilder))); + } +} diff --git a/tests/framework/db/sqlite/ColumnSchemaBuilderTest.php b/tests/framework/db/sqlite/ColumnSchemaBuilderTest.php index c8e7d5b0..56288962 100644 --- a/tests/framework/db/sqlite/ColumnSchemaBuilderTest.php +++ b/tests/framework/db/sqlite/ColumnSchemaBuilderTest.php @@ -1,9 +1,6 @@ getConnection()); + return new ColumnSchemaBuilder($this->getConnection(), $type, $length); } /** diff --git a/tests/framework/db/sqlite/QueryBuilderTest.php b/tests/framework/db/sqlite/QueryBuilderTest.php index fa3619ac..645b6564 100644 --- a/tests/framework/db/sqlite/QueryBuilderTest.php +++ b/tests/framework/db/sqlite/QueryBuilderTest.php @@ -22,17 +22,6 @@ class QueryBuilderTest extends \yiiunit\framework\db\QueryBuilderTest protected $likeEscapeCharSql = " ESCAPE '\\'"; - public function columnTypes() - { - return array_merge(parent::columnTypes(), [ - [ - Schema::TYPE_PK, - $this->primaryKey()->first()->after('col_before'), - 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', - ], - ]); - } - public function conditionProvider() { return array_merge(parent::conditionProvider(), [ diff --git a/tests/support/TableGenerator.php b/tests/support/TableGenerator.php new file mode 100644 index 00000000..d89c5efc --- /dev/null +++ b/tests/support/TableGenerator.php @@ -0,0 +1,52 @@ +getTableSchema($tableName, true) !== null) { + self::ensureNoTable($db, $tableName); + } + + return $db->createCommand()->createTable($tableName, $columns, $options)->execute(); + } + + /** + * @psalm-param array|class-string $fixtureClass + */ + public static function ensureTableWithFixture(Connection $db, array|string $fixtureClass): void + { + $fixture = new $fixtureClass($db); + + $tableName = $fixture->getName(); + + if ($db->getTableSchema($tableName, true) !== null) { + self::ensureNoTable($db, $tableName); + } + + $db->createCommand()->createTable( + $tableName, + $fixture->getColumns(), + $fixture->getOptions(), + )->execute(); + + foreach ($fixture->getPrimaryKeys() as $primaryKey) { + $db->createCommand()->addPrimaryKey("PK_{$tableName}", $tableName, $primaryKey)->execute(); + } + + foreach ($fixture->getData() as $data) { + $db->createCommand()->insert($tableName, $data)->execute(); + } + } + + public static function ensureNoTable(Connection $db, string $tableName): void + { + $db->createCommand()->dropTable($tableName)->execute(); + } +} diff --git a/tests/support/TestHelper.php b/tests/support/TestHelper.php new file mode 100644 index 00000000..0e54eb4c --- /dev/null +++ b/tests/support/TestHelper.php @@ -0,0 +1,29 @@ + $data) { + if (isset($dataProvider[$testName])) { + $result[$testName] = array_replace($dataProvider[$testName], $data); + } + } + + return $result; + } +}