Repository navigation
Conversation
CREATE USER and DEFINER read user@host with their own copies of the same rule. Neither accepted app@'%' or 'app'@localhost, because the lexer reads app@ and @localhost as single tokens. Without a dialect a double-quoted part also lost its quotes, so CREATE USER "app"@"%" rendered as app@%. Add one MySqlAccountName() production that splits those tokens, keep the quote character of double-quoted and backquoted parts apart from their value, and use it for CREATE USER and DEFINER. Signed-off-by: minleejae <mmj9808@gmail.com>
…ccounts Parse MySQL privilege levels (*, *.*, db.*), static and dynamic privileges, user@host accounts, quoted role grants, PROXY, REVOKE ALL PRIVILEGES, GRANT OPTION, REVOKE IF EXISTS ... IGNORE UNKNOWN USER and GRANT ... AS user WITH ROLE. Grantees, granted roles and the grantor use a typed Grantee/UserAccount model shared with PostgreSQL, which also gains membership option lists such as WITH INHERIT TRUE, SET FALSE. Accounts are read with the shared MySqlAccountName() rule, and WITH ROLE uses a RoleSelection next to UserAccount. Role memberships no longer carry an empty table target. The string accessors remain as derived views. Signed-off-by: minleejae <mmj9808@gmail.com>
This was referenced Oct 14, 2026
…ared user options Model the account options of CREATE USER and ALTER USER once in UserOptions (DEFAULT ROLE, REQUIRE, WITH resource limits, password and locking options, COMMENT/ATTRIBUTE), parsed by one shared production. Add ALTER USER, RENAME USER, MySQL CREATE ROLE names, SET ROLE, SET DEFAULT ROLE and SET PASSWORD, reading accounts and roles with the shared MySqlAccountName(), MySqlAccountNameList() and MySqlRoleSelection() productions. PostgreSQL ALTER USER and SET ROLE keep their existing AST. SET ROLE and SET PASSWORD without FOR become typed statements with the MySQL and MariaDB dialects. Signed-off-by: minleejae <mmj9808@gmail.com>
minleejae
force-pushed
the
feat/mysql-account-statements-20261014
branch
from
October 14, 2026 08:21
819970e to
f5b522a
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #2807, which builds on #2806. It uses their shared
MySqlAccountName(),MySqlAccountNameList()andMySqlRoleSelection()productions and theRoleSelectionclass.Purpose
Parse the MySQL/MariaDB account management statements. Today
ALTER USERfails in every form, evenALTER USER app IDENTIFIED BY 'x'.RENAME USER,SET DEFAULT ROLE,SET PASSWORD FOR …,SET ROLE ALL EXCEPT …andCREATE ROLE 'reader'/CREATE ROLE IF NOT EXISTS …fail as well.CREATE USERfails as soon as any option follows the accounts, for exampleDEFAULT ROLE,REQUIRE SSL,WITH MAX_QUERIES_PER_HOUR,PASSWORD EXPIRE,ACCOUNT LOCKorCOMMENT. Tools that provision, migrate or audit MySQL accounts cannot read this SQL.Current design (As-is)
CreateUserkeeps only the account list (UserAccountwithUserAuthentication). The grammar has no option clauses.ALTER USERgoes to PostgreSQL'sAlterRole.CREATE ROLEaccepts a single PostgreSQL name.SET ROLE xandSET PASSWORD = 'x'parse as a genericSetStatementthat assigns a variable namedROLEorPASSWORD. The other SET forms fail.Target design (To-be)
Shared options.
UserOptions(getOptions()onCreateUserand the newAlterUser) holds MySQL's clauses in their fixed order:DEFAULT ROLEas aRoleSelection;REQUIREasTlsOptions:NONE,SSL,X509,CIPHER/ISSUER/SUBJECT 'v', keeping the optionalAND;WITHasResourceLimits;PasswordLockOptions in source order. Each has a kind plus a number or a keyword (DEFAULT,NEVER,OPTIONAL,UNBOUNDED);COMMENTorATTRIBUTE.One production,
MySqlUserOptions, parses them for both statements. Omitted clauses stay omitted.Authentication.
UserAuthenticationaddsREPLACE 'current'(getCurrentCredential()),RETAIN CURRENT PASSWORDand the modeDISCARD_OLD_PASSWORD. These are accepted only after a password assignment.New statements
AlterUser:[IF EXISTS], accounts with authentication changes, and the shared options.ALTER USER USER() …keepsUSER()as the unquoted account name, asDEFINER = CURRENT_USERdoes;isUserFunction()identifies it.ALTER USER acct DEFAULT ROLE {NONE | ALL | role, …}stores the roles ingetOptions().getDefaultRoles().RenameUserwith orderedfrom/topairs.SetRoleStatement,SetDefaultRoleStatement(MySQLTO, MariaDBFORor no account) andSetPasswordStatement(= exprorTO RANDOM, withREPLACEandRETAIN CURRENT PASSWORD). The password is an expression, so MariaDB'sPASSWORD('x')keeps parsing.CreateRolegainsisIfNotExists()andgetRoles()for MySQL role names such as'r'and'r'@'h'.Account and role names come from the shared
MySqlAccountName()/MySqlAccountNameList(), so every spelling that CREATE USER accepts works here too. They areUserAccountvalues without authentication. The sharedRoleSelection(DEFAULT,NONE,ALL,ALL_EXCEPT,ROLES) is used by SET ROLE, SET DEFAULT ROLE and both DEFAULT ROLE clauses.Dispatch keeps PostgreSQL behavior
ALTER USERbecomes MySQL only forIF EXISTS,USER(), a quoted name, a host, an account list, no action, or a MySQL account clause.ALTER USER app WITH LOGIN,PASSWORD 'x',SET …,RENAME TOandALL RESET ALLremainAlterRole.CREATE USER app WITH MAX_…/PASSWORD EXPIREnow selectsCreateUser. These forms used to fail on the PostgreSQL path.WITH LOGIN,PASSWORD 'x'andDialect.POSTGRESQLstill selectCreateRole.CREATE ROLEuses the MySQL form only forIF NOT EXISTS, quoted names, hosts or a list. A single unquoted name keeps the PostgreSQLgetName(). So does an unquotedr@h, which the lexer reads as one identifier, except in the MySQL and MariaDB dialects.SET DEFAULT ROLE,SET PASSWORD FOR,SET ROLE ALL EXCEPT …and role names with a quoted user or host are typed in every dialect. They have no generic form.SET ROLE …andSET PASSWORD = …/TO RANDOMshare the variable-assignment shape that PostgreSQL uses (SET ROLE name,SET ROLE NONE). They are typed only withDialect.MYSQL/MARIADB; otherwise they remain aSetStatement.SET LOCAL ROLE …andSET role = …stay aSetStatementin every dialect.New reserved words: none. Option words are matched contextually, and the grammar check reports no choice conflicts.
Wiring
StatementVisitordefault methods and the adapter,StatementDeParser(the SET PASSWORD expression goes through the expression deparser) andTablesNamesFinder(no tables).alterUser,renameUser,setRole,setDefaultRoleandsetPassword. The MySQL and MariaDB capabilities enable them andcreateRole.StatementFeatureVisitor:MODIFIES_SCHEMA, orMODIFIES_SESSIONfor SET ROLE.Docs: a usage guide section, plus a README and index row.
Compatibility
Dialect.MYSQL/MARIADB,SET ROLE …andSET PASSWORD = …now return the typed statements instead ofSetStatement, andCREATE ROLE r@hfillsgetRoles(). Rendered SQL is unchanged.Not included
IDENTIFIED VIAandMAX_STATEMENT_TIME.CURRENT_USER()as an account.Validation
ALTER USER,CREATE USER,CREATE ROLEandSET ROLE;spotlessApply checkon top of Parse MySQL GRANT and REVOKE privilege levels, privileges and accounts #2807: 10,602 tests, 10,577 passed, 25 skipped, zero failures/errors. The grammar choice conflict check passes.Dialect.MYSQL:SET DEFAULT ROLE,SET ROLE ALL EXCEPTandSET PASSWORD FOR(16 statements, plus 10 variants), now parsed.SET ROLE 'reader',SET ROLE DEFAULT,SET PASSWORD = 'x'change fromSetStatementto the typed statements withDialect.MYSQLonly, as intended. They staySetStatementin default mode.toString()on a second, identical server, in the same order. All 250 MySQL and 328 PostgreSQL statements give the same result (success, or the same error).Integration validation
Applied #2806–#2819 together on
master(bdf8ac31), with the stacked PRs on top of their bases (#2806 → #2807 → #2808, #2806 → #2809 → #2810 and #2811 → #2812).master. Between the branches, conflicts are limited to adjacent additions (README/index rows, usage sections, imports, visitor and feature lists, one enum line and one alias guard).spotlessApply check: 11,494 tests, 11,469 passed, 25 skipped, zero failures/errors. The grammar choice conflict check passes.master: 206) and 363 of 364 PostgreSQL statements (master: 328). Every MySQL 8.4 non-reserved word and PostgreSQL 18 unreserved or column-name keyword parses as a column and table name.