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
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 #2806, which shares the MySQL account name rule (
MySqlAccountName()) ofCREATE USERandDEFINER.Purpose
Parse MySQL
GRANTandREVOKE, and give their privileges, privilege levels and accounts a typed model that MySQL and PostgreSQL share. Today only the simplest MySQL form parses (GRANT SELECT ON db1.t1 TO app). Common statements fail, for example:ON *.*,ON db1.*,ON *;TO 'app'@'%',TO `app`@`%`,TO app@'%',TO 'app';INDEX,CREATE VIEW,LOCK TABLESandBACKUP_ADMIN. The CDC grantGRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'%'fails withInvalid Kind: REPLICATION;PROXY,REVOKE ALL PRIVILEGES, GRANT OPTION,REVOKE IF EXISTS ... IGNORE UNKNOWN USERandGRANT ... AS user WITH ROLE ....PostgreSQL
GRANT r TO u WITH INHERIT TRUE, SET FALSE(PostgreSQL 16) also fails; a single option works.Current design (As-is)
PrivilegeClausekeeps grantees, roles and the grantor as strings.TO GROUP staffbecomes the grantee"GROUP staff".app@localhostpasses (the lexer reads it as one identifier).AccessPrivilege()accepts one token and maps it withPrivilege.Kind.valueOf. Multi-word and unknown privilege names fail, some withInvalid Kind.AccessTarget()accepts identifier lists only, so*levels fail.GRANT r TO u) carries an emptyPrivilegeTargetof kindTABLE.Grantholds a singleWITHoption.revoke.Target design (To-be)
PrivilegeTarget.getWildcard()returnsDEFAULT_DATABASE(*),GLOBAL(*.*) orDATABASE(db.*). The database ofdb.*is the single entry ofgetNames();*is never stored as a name part. Wildcards accept the optionalTABLEkeyword and no other object type. Table-name discovery skips wildcard levels.Privilege.Kindconstants, for exampleCREATE_TEMPORARY_TABLES,REPLICATION_SLAVE,SHOW_DATABASESandGRANT_OPTION. Any other identifier is a dynamic privilege: kindDYNAMIC, withgetDynamicName()as written.getName()returns the SQL spelling.Granteeis shared by MySQL and PostgreSQL. It is used for grantees, the PostgreSQL grantor, the MySQLASuser and the proxied account.ACCOUNTholds aUserAccount: a MySQLuser[@host]with its quoting, or a PostgreSQL role name without host.GROUP,PUBLIC,CURRENT_USER(alsoCURRENT_USER()),CURRENT_ROLEandSESSION_USER.MySqlAccountName()rule of Share MySQL account names across CREATE USER and DEFINER #2806, so every spelling thatCREATE USERaccepts works here too.getRoleItems()holdsUserAccountvalues, so'reader'and'writer'@'%'keep quoting and host. A parsed membership has no target (null).PROXY ON useris aPROXYprivilege on aPrivilegeTargetof the new kindACCOUNT.REVOKE ALL [PRIVILEGES], GRANT OPTION FROM ...has the privilegesALLandGRANT_OPTIONand no target.RevokerecordsIF EXISTSandIGNORE UNKNOWN USER.Grant.getAsUser()andgetWithRole()holdAS user [WITH ROLE DEFAULT | NONE | ALL [EXCEPT ...] | role, ...]. The role selection is the newRoleSelectioninstatement.create.user, next toUserAccount, so the MySQL account statements can reuse it.Grant.getOptions()lists everyWITHoption, including PostgreSQL membership options. Privileges take onlyGRANT OPTION; memberships take distinctADMIN,INHERITandSEToptions.MySqlAccountName():MySqlAccountNameList()andMySqlRoleSelection(boolean). They are meant for reuse by the MySQL account statements.Access*productions:AccessGrantee()(account orGROUP/PUBLIC/CURRENT_USER/CURRENT_ROLE/SESSION_USER),AccessGrantOption()andAccessGrantor().ALTER DEFAULT PRIVILEGES.StatementDeParserrender the new state.GrantValidatorvalidates named grantees and roles only, notPUBLICorCURRENT_USER.revoke.Compatibility: existing public methods keep their signatures and are derived from the typed state.
getUsers(),getGrantees()andgetRoles()are mutable string views that write through; assigned text is converted withGrantee.valueOf().getOption()returns the first option;setOption()replaces all options.getGrantedBy()andsetGrantedBy()render and convert the grantor.getObjectName()returns a wildcard level as written, for exampledb1.*;setObjectName()replaces it with a named target.new Privilege("CREATE VIEW")now yields the typed kind; the rendered SQL is unchanged.PUBLICandCURRENT_USERare rendered in upper case.setRoles()andsetGrantees()copy their input instead of keeping the caller's list.new Grant()still starts with an empty table target, andsetRole()clears it.Validation
MySqlGrantRevokeTest: 26 MySQL forms, each parsed, deparsed and reparsed withDialect.MYSQLand in the default mode. Also the AST of privilege levels, privileges, accounts,CURRENT_USER, role memberships,PROXY,REVOKE ALL PRIVILEGES, GRANT OPTION,IF EXISTS/IGNORE UNKNOWN USERandAS ... WITH ROLE; 23 rejected forms; typed builders; MySQL/MariaDB validation.GranteeModelTest: PostgreSQL role specifications and membership option lists withDialect.POSTGRESQL, rejected forms, and the legacy string API (write-through views, builder/parse object-tree equality, wildcard object names).ALTER DEFAULT PRIVILEGEStests pass unchanged.spotlessApply checkon top of Share MySQL account names across CREATE USER and DEFINER #2806: 10,474 tests, 10,449 passed, 25 skipped, zero failures/errors. The grammar choice conflict check passes.master(bdf8ac31) on a corpus of 635 MySQL 8.4 and PostgreSQL 18 DDL/DCL statements, each accepted by the server, plus variants:GRANT/REVOKEcases, 17 MySQL variants, 2user@'host'variants and PostgreSQLWITH INHERIT TRUE, SET FALSE. The 31 cases of the main MySQL and PostgreSQL corpora are also fixed without a dialect.GRANT,REVOKEandALTER DEFAULT PRIVILEGEScases changes, as intended: typed grantees, and no target for role memberships.toString()on a second, identical server, in the same order. All 235 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.