[RFC]: DDL standalone index, view, rename and truncate statements, and typed table options
Les mainteneurs répondent en général sous 1 jour
@simon-mundy y travaille déjà.
Depuis le 4/9/2026.
Évaluation
Cette issue n'a pas encore été évaluée.
Description
Proposed Version
0.6.0
Basic Information
Currently Sql\Ddl has exactly three statements — CreateTable, AlterTable, DropTable. Everything else a schema tool needs is either unreachable or only expressible through an untyped KEY = value option map. This proposal adds CreateIndex/DropIndex, RenameTable, AlterTable::renameColumn()/renameIndex(), Truncate and CreateView/DropView, and replaces the option map with validated keys, keyword enums and partitionBy(). Six independent PRs against this issue.
Background
| Statement | Today |
|---|---|
CREATE INDEX / DROP INDEX (standalone) |
only inside ALTER TABLE via addConstraint(new Index(...)) / dropIndex() |
RENAME TABLE |
nothing; AlterTable has no rename method |
ALTER TABLE ... RENAME COLUMN old TO new |
only changeColumn(), which renders CHANGE COLUMN old new <full definition> (AlterTable.php#L59-L63) and makes the caller restate the column |
ALTER TABLE ... RENAME INDEX |
nothing |
TRUNCATE TABLE |
nothing |
CREATE VIEW / DROP VIEW |
nothing, although Metadata reads views |
CreateTable::setOption() / AlterTable::setOption() (added in #139, only on unreleased 0.6.x) render each entry as strtoupper($key) . ' = ' . $value (CreateTable.php#L207-L229, AlterTable.php#L281-L303). Verified on MySQL 8.4.10 (reproduced on 8.0.46):
-- works: strings quoted (ENGINE, DEFAULT CHARSET, COLLATE, COMMENT), int raw (AUTO_INCREMENT), Literal raw (ROW_FORMAT)
) ENGINE = 'InnoDB' DEFAULT CHARSET = 'utf8mb4' COLLATE = 'utf8mb4_unicode_ci' COMMENT = 'it\'s' AUTO_INCREMENT = 1000 ROW_FORMAT = DYNAMIC
-- setOption('auto_increment', '1000'): ERROR 1064 (only the int path works for AUTO_INCREMENT)
) AUTO_INCREMENT = '1000'
-- setOption('row_format', 'DYNAMIC'): ERROR 1064 (keyword required, string gets quoted); same for 'algorithm' / 'lock' on AlterTable
) ROW_FORMAT = 'DYNAMIC'
-- setOption('partition by', new Literal('HASH(id) PARTITIONS 4')): ERROR 1064 (' = ' always inserted)
) PARTITION BY = HASH(id) PARTITIONS 4
-- setOption("engine = InnoDB; DROP TABLE y; -- ", 'x'): key is emitted raw
) ENGINE = INNODB; DROP TABLE Y; -- = 'x'
So keyword-valued options need a Literal the caller has to know about, PARTITION BY has no supported form (a Literal smuggled into another option's value carries it — a hack, not an API), ALGORITHM/LOCK on AlterTable work only via Literal, and the option key is a raw literal slot.
The phpdb-mysql-ddl-overrides branch on simon-mundy/phpdb-mysql has CreateIndex, DropIndex, TruncateTable, MysqlAlterTable (modifyColumn, renameColumn, renameTable, partitions), MysqlCreateTable, MysqlTableOptionsTrait and Partition, all in the adapter namespace. No views, no RenameTable, no renameIndex. It is pinned to a core branch that no longer exists and predates the literal-slot invariant, so it is a source of design and tests rather than something to merge.
Considerations
- Core or adapter? This proposes the statements in core, with the MySQL adapter registering a decorator only where MySQL syntax differs. The fork branch put them in the adapter. Agree on placement first.
- Identifier quoting. Core's
Sql92output double-quotes identifiers, which MySQL only accepts underANSI_QUOTES; backtick quoting comes from the adapter'sAdapterPlatform, as forDropTabletoday. "No decorator needed" means the grammar is the same, not that core output runs on MySQL unchanged. - Views.
AbstractSql::processSubSelect()only clones a decorator when the enclosing statement is itself a decorator, so the adapter needs a pass-throughCreateViewdecorator for theSelectto be decorated on MySQL. MySQL forbids parameter markers inside a view definition (ERROR 1351), soCreateViewinlines values and isgetSqlString()/query()-only. - Index reuse.
Ddl\Index\Indexhard-codesINDEX %s(...)in its spec, so sharing its rendering means extracting it or overriding the spec. NoUSINGfor FULLTEXT or SPATIAL (1064). - Table options.
setOption()only exists on unreleased0.6.x, so there is no released BC surface.ALGORITHM/LOCKare ALTER TABLE-only (1064 in CREATE TABLE).PARTITION BYhas to follow the table options. - Integration tests. The adapter has no DDL integration tests today (tables and views come from the raw
mysql.sqlfixture), so whichever PR lands first adds the class. IF NOT EXISTSonCreateTableandIF EXISTSonDropTablealready exist (#138) and are valid MySQL;AlterTablecorrectly emits neither. MariaDB-only forms (CREATE OR REPLACE TABLE,IF [NOT] EXISTSinsideALTER TABLE) are out of scope, as are column-level attributes (phpdb-mysql#81).
Proposal(s)
- PR 1:
CreateIndex/DropIndex.CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX name ON table (cols) [USING type](noUSINGfor FULLTEXT/SPATIAL) andDROP INDEX name ON table. Share column/length/type rendering withDdl\Index\Index. NoIF [NOT] EXISTS; MySQL has none for these. - PR 2:
RenameTable.RENAME TABLE a TO b [, c TO d];string|TableIdentifierpairs. - PR 3:
AlterTable::renameColumn(string $old, string $new)andrenameIndex(string $old, string $new).RENAME COLUMN old TO newandRENAME INDEX old TO new; MySQL 8.0 syntax and standard-shaped, no decorator needed. Both go ingetRawState(). - PR 4:
Truncate.TRUNCATE TABLE name. - PR 5:
CreateView/DropView.CREATE [OR REPLACE] VIEW name [(cols)] AS <Select>taking aSql\Select, optionalWITH [CASCADED|LOCAL] CHECK OPTION;DROP VIEW [IF EXISTS] name. Pass-through decorator in phpdb-mysql; values inlined. - PR 6: typed table options. Validate keys against
[A-Za-z_ ]+(or a backed enum of known options) and never emit an unvalidated key; addRowFormat,Algorithm,Lockbacked enums, widen theLiteral|bool|int|stringvalue union to take them, render them as keywords, and haveCreateTablerejectAlgorithm/Lock; addCreateTable::partitionBy(Literal|string $clause)renderingPARTITION BY <clause>after the other options with no=.ENGINE,DEFAULT CHARSET,COLLATE,COMMENTkeep working with quoted strings andAUTO_INCREMENTwith an int.
Test plan (per PR):
- Each new
Sql\Ddl\<Statement>extendsAbstractSql, implementsgetRawState()asCreateTable/AlterTabledo (DropTablehas none), and has a unit test pinning the rendered SQL underSql92quoting, plus one under the MySQL platform in phpdb-mysql if a decorator is added. docs/book/sql-ddl/gains a section per statement.- PR 6:
setOption('row_format', RowFormat::Dynamic)rendersROW_FORMAT = DYNAMIC;partitionBy(new Literal('HASH(id) PARTITIONS 4'))rendersPARTITION BY HASH(id) PARTITIONS 4; an invalid key throwsInvalidArgumentException;CreateTablerejectsAlgorithm/Lock; the existing option tests inCreateTableTestandAlterTableTestkeep passing. - Integration tests in phpdb-mysql execute each new statement against the CI MySQL image.
Appendix/Additional Info
- #175 (decorator contract), #169 (untyped option arrays), #139 (where table options were added), #138 (IF [NOT] EXISTS on Create/DropTable).
- From an internal DDL audit (not published — the findings are reproduced above).
- Langage dominant
- PHP
- Étoiles
- 18
- Forks
- 8
- Merge moyen
- 6 j 11 h
- PR mergées (30 j)
- 14
Préparer son environnement
- Fournit un Dockerfile ou un fichier Docker Compose
- Aucun modèle de pull request
- Aucun guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de php-db/phpdb
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
Les mainteneurs répondent en général sous 1 jour
-
RFC
Difficulté 3/5 1-2 jours Accessibilité débutants 66/100
Les mainteneurs répondent en général sous 1 jour
-
RFC
Difficulté 5/5 Plus d'une semaine Accessibilité débutants 38/100
Les mainteneurs répondent en général sous 1 jour
-
RFC
Difficulté 3/5 1-2 jours Accessibilité débutants 68/100
Les mainteneurs répondent en général sous 1 jour
-
RFC
Difficulté 4/5 3-5 jours Accessibilité débutants 45/100
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de php-db/phpdb
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
thephpleague/commonmark#1159 ·
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
awslabs/aidlc-workflows#1879 ·
Les mainteneurs répondent en général sous 1 jour
-
[Bug] The PHP file that lists DNS records truncates records to 12 characters?Peut-être pris @sahsanu l’a pris aujourd’hui. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
hestiacp/hestiacp#5769 · 2 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
bug customer-reported
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
MagnaCapax/PMSS#1011 ·
Les mainteneurs répondent en général sous 5 jours
-
Talk Review
Difficulté 2/5 1-3 heures Accessibilité débutants 66/100
socallinuxexpo/scale-drupal#351 ·