You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
0.6.0 for the enums, typed arguments and string-compat setters; the string-getter retype waits for the next major.
Basic Information
Every place under src/Sql/Ddl/ where a caller-supplied string becomes an Argument\Literal or is concatenated into a spec string gets a backed enum (closed value sets) or a typed int (numeric slots), with a one-minor string-compat path. This is the core counterpart of phpdb-mysql#79.
Background
The DDL layer renders three kinds of argument: Argument\Identifier (quoted as an identifier), Argument\Value (quoted or bound) and Argument\Literal (inserted verbatim). Going through every getExpressionData() under src/Sql/Ddl/ (and processTableOptions() for table options), these are the places where a Literal, or a bare string concatenated into the spec, comes from the caller rather than a class constant:
Example: setOption('row_format', 'DYNAMIC'), 'algorithm' and 'lock' with a plain string render a quoted value MySQL rejects with 1064. Only a Literal works, and nothing tells the caller that.
Safe by construction and not proposed to change: Column::$type (protected, set by the subclass), AbstractLengthColumn / AbstractPrecisionColumn lengths (built from ?int), AbstractTimestampColumn's fixed ON UPDATE CURRENT_TIMESTAMP, all identifier slots, Column defaults given as an Argument\Literal, and table-option values given as a Sql\Literal. Those last two are raw by definition.
Considerations
Docs bug. Only Argument\Literal itself says a Literal default is raw. Column::setDefault() and the DDL docs don't, and docs/book/sql-ddl/columns.md:340-346, alter-drop.md, examples.md and intro.md all show setDefault('CURRENT_TIMESTAMP') producing an unquoted default. It actually renders DEFAULT 'CURRENT_TIMESTAMP', which MySQL rejects (1067). Worth its own issue; listed here because it is the other side of the same invariant.
Getters.getOnDeleteRule(): string, getOnUpdateRule(): string and Index::getType(): ?string are asserted as strings in ForeignKeyTest.php:106-132 and IndexTest.php:67. Retyping them in a minor would be a BC break, so they stay until the next major.
SET DEFAULT is valid grammar that InnoDB doesn't honour (on 8.0.46 the DDL is accepted and RESTRICT is enforced at DML time). Keep it in the enum, document the caveat.
Index prefix length: MySQL rejects 0 with 1391, so positive-int is exactly the server's bound.
Integer display width is itself deprecated since 8.0.17; keeping it as a typed int is for the other platforms, and the MySQL decorator will drop it.
Setters keep accepting string for one minor, resolved with Enum::tryFrom(strtoupper(trim($value))) and rejected with InvalidArgumentException when unknown. Passing a string is deprecated in the docblock and removed at the next major.
Getters keep returning string for the same period. Add enum-returning siblings now (getOnDeleteAction(): ReferentialAction, getOnUpdateAction(): ReferentialAction, Index::getIndexType(): ?IndexType) and retype the string getters at the next major.
Test plan:
No getExpressionData() under src/Sql/Ddl/ constructs an Argument\Literal from a caller string except Check (documented) and Column defaults (documented in setDefault() and the docs, with the CURRENT_TIMESTAMP examples corrected).
No getExpressionData() or processTableOptions() concatenates a caller value into the spec string.
Each enum has a unit test for accepted values, the string-compat path and rejection.
The probe strings above render quoted/escaped or throw; none reaches SQL verbatim.
Proposed Version
0.6.0 for the enums, typed arguments and string-compat setters; the string-getter retype waits for the next major.
Basic Information
Every place under
src/Sql/Ddl/where a caller-supplied string becomes anArgument\Literalor is concatenated into a spec string gets a backed enum (closed value sets) or a typedint(numeric slots), with a one-minor string-compat path. This is the core counterpart of phpdb-mysql#79.Background
The DDL layer renders three kinds of argument:
Argument\Identifier(quoted as an identifier),Argument\Value(quoted or bound) andArgument\Literal(inserted verbatim). Going through everygetExpressionData()undersrc/Sql/Ddl/(andprocessTableOptions()for table options), these are the places where aLiteral, or a bare string concatenated into the spec, comes from the caller rather than a class constant:Indexarray $lengths, concatenated into the specINDEX `i`(`email`(20) INJECT))IndexUSINGtypesetType(string)->LiteralUSING HASH; DROPForeignKeyON DELETE/ON UPDATErulestringsetters ->LiteralON DELETE CASCADE; DROPIntegeroptions['length'](untyped via the constructor array,bool|stringviasetOption()), concatenatedINTEGER NOT NULL (11)(also a positional bug, #178)Checkstring|ExpressionInterfacedocblock ->LiteralExpressionis accepted at construction and fails at render (#177)CreateTable/AlterTableprocessTableOptions()setOption(string $name, ...),strtoupper()onlyENGINE = INNODB; DROP TABLE Y; -- = 'x'Example:
setOption('row_format', 'DYNAMIC'),'algorithm'and'lock'with a plain string render a quoted value MySQL rejects with 1064. Only aLiteralworks, and nothing tells the caller that.Safe by construction and not proposed to change:
Column::$type(protected, set by the subclass),AbstractLengthColumn/AbstractPrecisionColumnlengths (built from?int),AbstractTimestampColumn's fixedON UPDATE CURRENT_TIMESTAMP, all identifier slots,Columndefaults given as anArgument\Literal, and table-option values given as aSql\Literal. Those last two are raw by definition.Considerations
Argument\Literalitself says aLiteraldefault is raw.Column::setDefault()and the DDL docs don't, anddocs/book/sql-ddl/columns.md:340-346,alter-drop.md,examples.mdandintro.mdall showsetDefault('CURRENT_TIMESTAMP')producing an unquoted default. It actually rendersDEFAULT 'CURRENT_TIMESTAMP', which MySQL rejects (1067). Worth its own issue; listed here because it is the other side of the same invariant.getOnDeleteRule(): string,getOnUpdateRule(): stringandIndex::getType(): ?stringare asserted as strings inForeignKeyTest.php:106-132andIndexTest.php:67. Retyping them in a minor would be a BC break, so they stay until the next major.SET DEFAULTis valid grammar that InnoDB doesn't honour (on 8.0.46 the DDL is accepted and RESTRICT is enforced at DML time). Keep it in the enum, document the caveat.positive-intis exactly the server's bound.intis for the other platforms, and the MySQL decorator will drop it.RowFormat/Algorithm/Lockand key validation live there).Proposal(s)
Sql\Ddl\Constraint\ReferentialAction: string { NoAction = 'NO ACTION'; Restrict = 'RESTRICT'; Cascade = 'CASCADE'; SetNull = 'SET NULL'; SetDefault = 'SET DEFAULT' }forForeignKey::setOnDeleteRule()/setOnUpdateRule()and the constructor.Sql\Ddl\Index\IndexType: string { BTree = 'BTREE'; Hash = 'HASH' }forIndex::setType().Index::$lengthstypedlist<positive-int>(validated in the constructor) and rendered asLiteral((string) $int)in the values array, not in the spec.Integerlength asint(see Integerlengthoption renders the display width after NOT NULL #178).quoteTrustedValue(), keyword values throughRowFormat/Algorithm/Lockenums ([RFC]: DDL standalone index, view, rename and truncate statements, and typed table options #179).Compatibility:
stringfor one minor, resolved withEnum::tryFrom(strtoupper(trim($value)))and rejected withInvalidArgumentExceptionwhen unknown. Passing a string is deprecated in the docblock and removed at the next major.stringfor the same period. Add enum-returning siblings now (getOnDeleteAction(): ReferentialAction,getOnUpdateAction(): ReferentialAction,Index::getIndexType(): ?IndexType) and retype the string getters at the next major.Test plan:
getExpressionData()undersrc/Sql/Ddl/constructs anArgument\Literalfrom a caller string exceptCheck(documented) andColumndefaults (documented insetDefault()and the docs, with theCURRENT_TIMESTAMPexamples corrected).getExpressionData()orprocessTableOptions()concatenates a caller value into the spec string.Appendix/Additional Info