%{ // Copyright 2013 The ql Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSES/QL-LICENSE file. // Copyright 2015 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. // Initial yacc source generated by ebnf2y[1] // at 2013-10-04 23:10:47.861401015 +0200 CEST // // $ ebnf2y -o ql.y -oe ql.ebnf -start StatementList -pkg ql -p _ // // [1]: http://github.com/cznic/ebnf2y package parser import ( "strings" "github.com/pingcap/parser/mysql" "github.com/pingcap/parser/ast" "github.com/pingcap/parser/model" "github.com/pingcap/parser/opcode" "github.com/pingcap/parser/auth" "github.com/pingcap/parser/charset" "github.com/pingcap/parser/types" ) %} %union { offset int // offset item interface{} ident string expr ast.ExprNode statement ast.StmtNode } %token /*yy:token "%c" */ identifier "identifier" /*yy:token "_%c" */ underscoreCS "UNDERSCORE_CHARSET" /*yy:token "\"%c\"" */ stringLit "string literal" singleAtIdentifier "identifier with single leading at" doubleAtIdentifier "identifier with double leading at" invalid "a special token never used by parser, used by lexer to indicate error" hintBegin "hintBegin is a virtual token for optimizer hint grammar" hintEnd "hintEnd is a virtual token for optimizer hint grammar" andand "&&" pipes "||" /* The following tokens belong to ODBCDateTimeType. */ odbcDateType "d" odbcTimeType "t" odbcTimestampType "ts" /* The following tokens belong to ReservedKeyword. Notice: make sure these tokens are contained in ReservedKeyword. */ add "ADD" all "ALL" alter "ALTER" analyze "ANALYZE" and "AND" as "AS" asc "ASC" between "BETWEEN" bigIntType "BIGINT" binaryType "BINARY" blobType "BLOB" both "BOTH" by "BY" cascade "CASCADE" caseKwd "CASE" change "CHANGE" character "CHARACTER" charType "CHAR" check "CHECK" collate "COLLATE" column "COLUMN" constraint "CONSTRAINT" convert "CONVERT" create "CREATE" cross "CROSS" cumeDist "CUME_DIST" currentDate "CURRENT_DATE" currentTime "CURRENT_TIME" currentTs "CURRENT_TIMESTAMP" currentUser "CURRENT_USER" currentRole "CURRENT_ROLE" database "DATABASE" databases "DATABASES" dayHour "DAY_HOUR" dayMicrosecond "DAY_MICROSECOND" dayMinute "DAY_MINUTE" daySecond "DAY_SECOND" decimalType "DECIMAL" defaultKwd "DEFAULT" delayed "DELAYED" deleteKwd "DELETE" denseRank "DENSE_RANK" desc "DESC" describe "DESCRIBE" distinct "DISTINCT" distinctRow "DISTINCTROW" div "DIV" doubleType "DOUBLE" drop "DROP" dual "DUAL" elseKwd "ELSE" enclosed "ENCLOSED" escaped "ESCAPED" exists "EXISTS" explain "EXPLAIN" except "EXCEPT" falseKwd "FALSE" firstValue "FIRST_VALUE" floatType "FLOAT" forKwd "FOR" force "FORCE" foreign "FOREIGN" from "FROM" fulltext "FULLTEXT" generated "GENERATED" grant "GRANT" group "GROUP" groups "GROUPS" having "HAVING" highPriority "HIGH_PRIORITY" hourMicrosecond "HOUR_MICROSECOND" hourMinute "HOUR_MINUTE" hourSecond "HOUR_SECOND" ifKwd "IF" ignore "IGNORE" in "IN" index "INDEX" infile "INFILE" inner "INNER" integerType "INTEGER" interval "INTERVAL" into "INTO" is "IS" insert "INSERT" intType "INT" int1Type "INT1" int2Type "INT2" int3Type "INT3" int4Type "INT4" int8Type "INT8" join "JOIN" key "KEY" keys "KEYS" kill "KILL" lag "LAG" lastValue "LAST_VALUE" lead "LEAD" leading "LEADING" left "LEFT" like "LIKE" limit "LIMIT" lines "LINES" linear "LINEAR" load "LOAD" localTime "LOCALTIME" localTs "LOCALTIMESTAMP" lock "LOCK" longblobType "LONGBLOB" longtextType "LONGTEXT" lowPriority "LOW_PRIORITY" match "MATCH" maxValue "MAXVALUE" mediumblobType "MEDIUMBLOB" mediumIntType "MEDIUMINT" mediumtextType "MEDIUMTEXT" minuteMicrosecond "MINUTE_MICROSECOND" minuteSecond "MINUTE_SECOND" mod "MOD" not "NOT" noWriteToBinLog "NO_WRITE_TO_BINLOG" nthValue "NTH_VALUE" ntile "NTILE" null "NULL" numericType "NUMERIC" nvarcharType "NVARCHAR" on "ON" optimize "OPTIMIZE" option "OPTION" optionally "OPTIONALLY" or "OR" order "ORDER" outer "OUTER" over "OVER" packKeys "PACK_KEYS" partition "PARTITION" parser "PARSER" percentRank "PERCENT_RANK" precisionType "PRECISION" primary "PRIMARY" procedure "PROCEDURE" shardRowIDBits "SHARD_ROW_ID_BITS" preSplitRegions "PRE_SPLIT_REGIONS" rangeKwd "RANGE" rank "RANK" read "READ" realType "REAL" references "REFERENCES" regexpKwd "REGEXP" rename "RENAME" repeat "REPEAT" replace "REPLACE" require "REQUIRE" restrict "RESTRICT" revoke "REVOKE" right "RIGHT" rlike "RLIKE" row "ROW" rows "ROWS" rowNumber "ROW_NUMBER" secondMicrosecond "SECOND_MICROSECOND" selectKwd "SELECT" set "SET" show "SHOW" smallIntType "SMALLINT" spatial "SPATIAL" sql "SQL" sqlBigResult "SQL_BIG_RESULT" sqlCalcFoundRows "SQL_CALC_FOUND_ROWS" sqlSmallResult "SQL_SMALL_RESULT" ssl "SSL" starting "STARTING" straightJoin "STRAIGHT_JOIN" tableKwd "TABLE" stored "STORED" terminated "TERMINATED" then "THEN" tinyblobType "TINYBLOB" tinyIntType "TINYINT" tinytextType "TINYTEXT" to "TO" trailing "TRAILING" trigger "TRIGGER" trueKwd "TRUE" unique "UNIQUE" union "UNION" unlock "UNLOCK" unsigned "UNSIGNED" update "UPDATE" usage "USAGE" use "USE" using "USING" utcDate "UTC_DATE" utcTimestamp "UTC_TIMESTAMP" utcTime "UTC_TIME" values "VALUES" long "LONG" varcharType "VARCHAR" varcharacter "VARCHARACTER" varbinaryType "VARBINARY" varying "VARYING" virtual "VIRTUAL" when "WHEN" where "WHERE" write "WRITE" window "WINDOW" with "WITH" xor "XOR" yearMonth "YEAR_MONTH" zerofill "ZEROFILL" natural "NATURAL" /* The following tokens belong to UnReservedKeyword. Notice: make sure these tokens are contained in UnReservedKeyword. */ account "ACCOUNT" action "ACTION" after "AFTER" always "ALWAYS" algorithm "ALGORITHM" any "ANY" ascii "ASCII" autoIncrement "AUTO_INCREMENT" avgRowLength "AVG_ROW_LENGTH" avg "AVG" begin "BEGIN" binlog "BINLOG" bitType "BIT" block "BLOCK" booleanType "BOOLEAN" boolType "BOOL" btree "BTREE" byteType "BYTE" cascaded "CASCADED" charsetKwd "CHARSET" checksum "CHECKSUM" cipher "CIPHER" cleanup "CLEANUP" client "CLIENT" coalesce "COALESCE" collation "COLLATION" columnFormat "COLUMN_FORMAT" columns "COLUMNS" comment "COMMENT" commit "COMMIT" committed "COMMITTED" compact "COMPACT" compressed "COMPRESSED" compression "COMPRESSION" connection "CONNECTION" consistent "CONSISTENT" context "CONTEXT" cpu "CPU" current "CURRENT" day "DAY" data "DATA" dateType "DATE" datetimeType "DATETIME" deallocate "DEALLOCATE" definer "DEFINER" delayKeyWrite "DELAY_KEY_WRITE" directory "DIRECTORY" disable "DISABLE" discard "DISCARD" disk "DISK" do "DO" duplicate "DUPLICATE" dynamic "DYNAMIC" enable "ENABLE" encryption "ENCRYPTION" end "END" engine "ENGINE" engines "ENGINES" enum "ENUM" event "EVENT" events "EVENTS" escape "ESCAPE" exchange "EXCHANGE" exclusive "EXCLUSIVE" execute "EXECUTE" expire "EXPIRE" faultsSym "FAULTS" fields "FIELDS" first "FIRST" fixed "FIXED" flush "FLUSH" following "FOLLOWING" format "FORMAT" full "FULL" function "FUNCTION" grants "GRANTS" hash "HASH" history "HISTORY" hour "HOUR" identified "IDENTIFIED" importKwd "IMPORT" insertMethod "INSERT_METHOD" isolation "ISOLATION" issuer "ISSUER" incremental "INCREMENTAL" indexes "INDEXES" invisible "INVISIBLE" invoker "INVOKER" io "IO" ipc "IPC" jsonType "JSON" keyBlockSize "KEY_BLOCK_SIZE" labels "LABELS" last "LAST" less "LESS" level "LEVEL" list "LIST" local "LOCAL" location "LOCATION" master "MASTER" microsecond "MICROSECOND" minute "MINUTE" mode "MODE" modify "MODIFY" month "MONTH" maxRows "MAX_ROWS" maxConnectionsPerHour "MAX_CONNECTIONS_PER_HOUR" maxQueriesPerHour "MAX_QUERIES_PER_HOUR" maxUpdatesPerHour "MAX_UPDATES_PER_HOUR" maxUserConnections "MAX_USER_CONNECTIONS" memory "MEMORY" merge "MERGE" minRows "MIN_ROWS" names "NAMES" national "NATIONAL" ncharType "NCHAR" never "NEVER" no "NO" nodegroup "NODEGROUP" none "NONE" nulls "NULLS" offset "OFFSET" only "ONLY" pageSym "PAGE" password "PASSWORD" partial "PARTIAL" partitioning "PARTITIONING" partitions "PARTITIONS" pipesAsOr plugins "PLUGINS" preceding "PRECEDING" prepare "PREPARE" privileges "PRIVILEGES" process "PROCESS" processlist "PROCESSLIST" profile "PROFILE" profiles "PROFILES" quarter "QUARTER" query "QUERY" queries "QUERIES" quick "QUICK" rebuild "REBUILD" recover "RECOVER" redundant "REDUNDANT" reload "RELOAD" remove "REMOVE" reorganize "REORGANIZE" repair "REPAIR" repeatable "REPEATABLE" respect "RESPECT" replica "REPLICA" replication "REPLICATION" reverse "REVERSE" role "ROLE" rollback "ROLLBACK" routine "ROUTINE" rowCount "ROW_COUNT" rowFormat "ROW_FORMAT" rtree "RTREE" second "SECOND" secondaryEngine "SECONDARY_ENGINE" secondaryLoad "SECONDARY_LOAD" secondaryUnload "SECONDARY_UNLOAD" security "SECURITY" separator "SEPARATOR" serial "SERIAL" serializable "SERIALIZABLE" session "SESSION" share "SHARE" shared "SHARED" shutdown "SHUTDOWN" signed "SIGNED" simple "SIMPLE" slave "SLAVE" slow "SLOW" snapshot "SNAPSHOT" sqlBufferResult "SQL_BUFFER_RESULT" sqlCache "SQL_CACHE" sqlNoCache "SQL_NO_CACHE" sqlTsiDay "SQL_TSI_DAY" sqlTsiHour "SQL_TSI_HOUR" sqlTsiMinute "SQL_TSI_MINUTE" sqlTsiMonth "SQL_TSI_MONTH" sqlTsiQuarter "SQL_TSI_QUARTER" sqlTsiSecond "SQL_TSI_SECOND" sqlTsiWeek "SQL_TSI_WEEK" sqlTsiYear "SQL_TSI_YEAR" start "START" statsAutoRecalc "STATS_AUTO_RECALC" statsPersistent "STATS_PERSISTENT" statsSamplePages "STATS_SAMPLE_PAGES" status "STATUS" storage "STORAGE" swaps "SWAPS" switchesSym "SWITCHES" systemTime "SYSTEM_TIME" open "OPEN" source "SOURCE" subject "SUBJECT" subpartition "SUBPARTITION" subpartitions "SUBPARTITIONS" super "SUPER" some "SOME" global "GLOBAL" tableChecksum "TABLE_CHECKSUM" tables "TABLES" tablespace "TABLESPACE" temporary "TEMPORARY" temptable "TEMPTABLE" textType "TEXT" than "THAN" timeType "TIME" timestampType "TIMESTAMP" trace "TRACE" traditional "TRADITIONAL" transaction "TRANSACTION" triggers "TRIGGERS" truncate "TRUNCATE" tp "TYPE" unbounded "UNBOUNDED" uncommitted "UNCOMMITTED" unicodeSym "UNICODE" unknown "UNKNOWN" user "USER" undefined "UNDEFINED" validation "VALIDATION" value "VALUE" variables "VARIABLES" view "VIEW" visible "VISIBLE" binding "BINDING" bindings "BINDINGS" warnings "WARNINGS" without "WITHOUT" identSQLErrors "ERRORS" week "WEEK" yearType "YEAR" x509 "X509" enforced "ENFORCED" nowait "NOWAIT" /* The following tokens belong to NotKeywordToken. Notice: make sure these tokens are contained in NotKeywordToken. */ addDate "ADDDATE" bitAnd "BIT_AND" bitOr "BIT_OR" bitXor "BIT_XOR" cast "CAST" copyKwd "COPY" count "COUNT" curTime "CURTIME" dateAdd "DATE_ADD" dateSub "DATE_SUB" extract "EXTRACT" getFormat "GET_FORMAT" groupConcat "GROUP_CONCAT" next_row_id "NEXT_ROW_ID" inplace "INPLACE" instant "INSTANT" internal "INTERNAL" min "MIN" max "MAX" maxExecutionTime "MAX_EXECUTION_TIME" now "NOW" position "POSITION" recent "RECENT" std "STD" stddev "STDDEV" stddevPop "STDDEV_POP" stddevSamp "STDDEV_SAMP" subDate "SUBDATE" sum "SUM" substring "SUBSTRING" timestampAdd "TIMESTAMPADD" timestampDiff "TIMESTAMPDIFF" tokudbDefault "TOKUDB_DEFAULT" tokudbFast "TOKUDB_FAST" tokudbLzma "TOKUDB_LZMA" tokudbQuickLZ "TOKUDB_QUICKLZ" tokudbSnappy "TOKUDB_SNAPPY" tokudbSmall "TOKUDB_SMALL" tokudbUncompressed "TOKUDB_UNCOMPRESSED" tokudbZlib "TOKUDB_ZLIB" top "TOP" trim "TRIM" variance "VARIANCE" varPop "VAR_POP" varSamp "VAR_SAMP" exprPushdownBlacklist "EXPR_PUSHDOWN_BLACKLIST" optRuleBlacklist "OPT_RULE_BLACKLIST" /* The following tokens belong to TiDBKeyword. Notice: make sure these tokens are contained in TiDBKeyword. */ admin "ADMIN" buckets "BUCKETS" builtins "BUILTINS" cancel "CANCEL" cmSketch "CMSKETCH" ddl "DDL" depth "DEPTH" drainer "DRAINER" jobs "JOBS" job "JOB" nodeID "NODE_ID" nodeState "NODE_STATE" optimistic "OPTIMISTIC" pessimistic "PESSIMISTIC" pump "PUMP" samples "SAMPLES" stats "STATS" statsMeta "STATS_META" statsHistograms "STATS_HISTOGRAMS" statsBuckets "STATS_BUCKETS" statsHealthy "STATS_HEALTHY" tidb "TIDB" hintAggToCop "AGG_TO_COP" hintHJ "HASH_JOIN" hintSMJ "SM_JOIN" hintINLJ "INL_JOIN" hintHASHAGG "HASH_AGG" hintSTREAMAGG "STREAM_AGG" hintUseIndex "USE_INDEX" hintIgnoreIndex "IGNORE_INDEX" hintUseIndexMerge "USE_INDEX_MERGE" hintNoIndexMerge "NO_INDEX_MERGE" hintUseToja "USE_TOJA" hintEnablePlanCache "ENABLE_PLAN_CACHE" hintUsePlanCache "USE_PLAN_CACHE" hintReadConsistentReplica "READ_CONSISTENT_REPLICA" hintReadFromStorage "READ_FROM_STORAGE" hintQBName "QB_NAME" hintQueryType "QUERY_TYPE" hintMemoryQuota "MEMORY_QUOTA" hintOLAP "OLAP" hintOLTP "OLTP" hintTiKV "TIKV" hintTiFlash "TIFLASH" topn "TOPN" split "SPLIT" width "WIDTH" regions "REGIONS" region "REGION" builtinAddDate builtinBitAnd builtinBitOr builtinBitXor builtinCast builtinCount builtinCurDate builtinCurTime builtinDateAdd builtinDateSub builtinExtract builtinGroupConcat builtinMax builtinMin builtinNow builtinPosition builtinSubDate builtinSubstring builtinSum builtinSysDate builtinStddevPop builtinStddevSamp builtinTrim builtinUser builtinVarPop builtinVarSamp %token /*yy:token "1.%d" */ floatLit "floating-point literal" /*yy:token "1.%d" */ decLit "decimal literal" /*yy:token "%d" */ intLit "integer literal" /*yy:token "%x" */ hexLit "hexadecimal literal" /*yy:token "%b" */ bitLit "bit literal" andnot "&^" assignmentEq ":=" eq "=" ge ">=" le "<=" jss "->" juss "->>" lsh "<<" neq "!=" neqSynonym "<>" nulleq "<=>" paramMarker "?" rsh ">>" %token not2 %type Expression "expression" MaxValueOrExpression "maxvalue or expression" BoolPri "boolean primary expression" ExprOrDefault "expression or default" PredicateExpr "Predicate expression factor" SetExpr "Set variable statement value's expression" BitExpr "bit expression" SimpleExpr "simple expression" SimpleIdent "Simple Identifier expression" SumExpr "aggregate functions" FunctionCallGeneric "Function call with Identifier" FunctionCallKeyword "Function call with keyword as function name" FunctionCallNonKeyword "Function call with nonkeyword as function name" Literal "literal value" Variable "User or system variable" SystemVariable "System defined variable name" UserVariable "User defined variable name" SubSelect "Sub Select" StringLiteral "text literal" ExpressionOpt "Optional expression" SignedLiteral "Literal or NumLiteral with sign" DefaultValueExpr "DefaultValueExpr(Now or Signed Literal)" NowSymOptionFraction "NowSym with optional fraction part" CharsetNameOrDefault "Character set name or default" %type AdminStmt "Check table statement or show ddl statement" AlterDatabaseStmt "Alter database statement" AlterTableStmt "Alter table statement" AlterUserStmt "Alter user statement" AnalyzeTableStmt "Analyze table statement" BeginTransactionStmt "BEGIN TRANSACTION statement" BinlogStmt "Binlog base64 statement" CommitStmt "COMMIT statement" CreateTableStmt "CREATE TABLE statement" CreateViewStmt "CREATE VIEW stetement" CreateUserStmt "CREATE User statement" CreateRoleStmt "CREATE Role statement" CreateDatabaseStmt "Create Database Statement" CreateIndexStmt "CREATE INDEX statement" CreateBindingStmt "CREATE BINDING statement" DoStmt "Do statement" DropDatabaseStmt "DROP DATABASE statement" DropIndexStmt "DROP INDEX statement" DropStatsStmt "DROP STATS statement" DropTableStmt "DROP TABLE statement" DropUserStmt "DROP USER" DropRoleStmt "DROP ROLE" DropViewStmt "DROP VIEW statement" DropBindingStmt "DROP BINDING statement" DeallocateStmt "Deallocate prepared statement" DeleteFromStmt "DELETE FROM statement" EmptyStmt "empty statement" ExecuteStmt "Execute statement" ExplainStmt "EXPLAIN statement" ExplainableStmt "explainable statement" FlushStmt "Flush statement" GrantStmt "Grant statement" GrantRoleStmt "Grant role statement" InsertIntoStmt "INSERT INTO statement" KillStmt "Kill statement" LoadDataStmt "Load data statement" LoadStatsStmt "Load statistic statement" LockTablesStmt "Lock tables statement" PreparedStmt "PreparedStmt" SelectStmt "SELECT statement" RenameTableStmt "rename table statement" ReplaceIntoStmt "REPLACE INTO statement" RecoverTableStmt "recover table statement" RevokeStmt "Revoke statement" RevokeRoleStmt "Revoke role statement" RollbackStmt "ROLLBACK statement" SplitRegionStmt "Split index region statement" SetStmt "Set variable statement" ChangeStmt "Change statement" SetRoleStmt "Set active role statement" SetDefaultRoleStmt "Set default statement for some user" ShowStmt "Show engines/databases/tables/user/columns/warnings/status statement" Statement "statement" TraceStmt "TRACE statement" TraceableStmt "traceable statement" TruncateTableStmt "TRUNCATE TABLE statement" UnlockTablesStmt "Unlock tables statement" UpdateStmt "UPDATE statement" UnionStmt "Union select state ment" UseStmt "USE statement" ShutdownStmt "SHUTDOWN statement" %type AdminShowSlow "Admin Show Slow statement" AllOrPartitionNameList "All or partition name list" AlgorithmClause "Alter table algorithm" AlterTablePartitionOpt "Alter table partition option" AlterTableSpec "Alter table specification" AlterTableSpecList "Alter table specification list" AlterTableSpecListOpt "Alter table specification list optional" AnalyzeOption "Analyze option" AnalyzeOptionList "Analyze option list" AnalyzeOptionListOpt "Optional analyze option list" AnyOrAll "Any or All for subquery" Assignment "assignment" AssignmentList "assignment list" AssignmentListOpt "assignment list opt" AuthOption "User auth option" AuthString "Password string value" OptionalBraces "optional braces" CastType "Cast function target type" CharsetName "Character set name" ClearPasswordExpireOptions "Clear password expire options" CollationName "Collation name" ColumnDef "table column definition" ColumnDefList "table column definition list" ColumnFormat "Column format" ColumnName "column name" ColumnNameOrUserVariable "column name or user variable" ColumnNameList "column name list" ColumnNameOrUserVariableList "column name or user variable list" ColumnList "column list" ColumnNameListOpt "column name list opt" ColumnNameOrUserVarListOpt "column name or user vairiabe list opt" ColumnNameOrUserVarListOptWithBrackets "column name or user variable list opt with brackets" ColumnSetValue "insert statement set value by column name" ColumnSetValueList "insert statement set value by column name list" CompareOp "Compare opcode" ColumnOption "column definition option" ColumnOptionList "column definition option list" VirtualOrStored "indicate generated column is stored or not" ColumnOptionListOpt "optional column definition option list" ConnectionOption "single connection options" ConnectionOptionList "connection options for CREATE USER statement" ConnectionOptions "optional connection options for CREATE USER statement" Constraint "table constraint" ConstraintElem "table constraint element" ConstraintKeywordOpt "Constraint Keyword or empty" CreateTableOptionListOpt "create table option list opt" CreateTableSelectOpt "Select/Union statement in CREATE TABLE ... SELECT" CreateViewSelectOpt "Select/Union statement in CREATE VIEW ... AS SELECT" DatabaseOption "CREATE Database specification" DatabaseOptionList "CREATE Database specification list" DatabaseOptionListOpt "CREATE Database specification list opt" DBName "Database Name" DistinctOpt "Explicit distinct option" DefaultFalseDistinctOpt "Distinct option which defaults to false" DefaultTrueDistinctOpt "Distinct option which defaults to true" BuggyDefaultFalseDistinctOpt "Distinct option which accepts DISTINCT ALL and defaults to false" RequireClause "Encrypted connections options" RequireClauseOpt "optional Encrypted connections options" EqOpt "= or empty" EscapedTableRef "escaped table reference" ExplainFormatType "explain format type" ExpressionList "expression list" MaxValueOrExpressionList "maxvalue or expression list" ExpressionListOpt "expression list opt" FuncDatetimePrecListOpt "Function datetime precision list opt" FuncDatetimePrecList "Function datetime precision list" Field "field expression" Fields "Fields clause" FieldAsName "Field alias name" FieldAsNameOpt "Field alias name opt" FieldList "field expression list" FieldTerminator "Field terminator" FlushOption "Flush option" PluginNameList "Plugin Name List" TableRefsClause "Table references clause" FieldItem "Field item for load data clause" FieldItemList "Field items for load data clause" FuncDatetimePrec "Function datetime precision" GetFormatSelector "{DATE|DATETIME|TIME|TIMESTAMP}" GlobalScope "The scope of variable" GroupByClause "GROUP BY clause" HashString "Hashed string" HavingClause "HAVING clause" HandleRange "handle range" HandleRangeList "handle range list" IfExists "If Exists" IfNotExists "If Not Exists" IgnoreOptional "IGNORE or empty" IndexColName "Index column name" IndexColNameList "List of index column name" IndexColNameListOpt "List of index column name opt" IndexHint "index hint" IndexHintList "index hint list" IndexHintListOpt "index hint list opt" IndexHintScope "index hint scope" IndexHintType "index hint type" IndexInvisible "index visible/invisible" IndexKeyTypeOpt "index key type" IndexLockAndAlgorithmOpt "index lock and algorithm" IndexName "index name" IndexNameAndTypeOpt "index name and index type" IndexNameList "index name list" IndexOption "Index Option" IndexOptionList "Index Option List or empty" IndexType "index type" IndexTypeName "index type name" IndexTypeOpt "optional index type" InsertValues "Rest part of INSERT/REPLACE INTO statement" JoinTable "join table" JoinType "join type" KillOrKillTiDB "Kill or Kill TiDB" LocationLabelList "location label name list" LikeEscapeOpt "like escape option" LikeTableWithOrWithoutParen "LIKE table_name or ( LIKE table_name )" LimitClause "LIMIT clause" LimitOption "Limit option could be integer or parameter marker." Lines "Lines clause" LinesTerminated "Lines terminated by" LoadDataSetSpecOpt "Optional load data specification" LoadDataSetList "Load data specifications" LoadDataSetItem "Single load data specification" LocalOpt "Local opt" LockClause "Alter table lock clause" NumLiteral "Num/Int/Float/Decimal Literal" NoWriteToBinLogAliasOpt "NO_WRITE_TO_BINLOG alias LOCAL or empty" ObjectType "Grant statement object type" OnDuplicateKeyUpdate "ON DUPLICATE KEY UPDATE value list" DuplicateOpt "[IGNORE|REPLACE] in CREATE TABLE ... SELECT statement or LOAD DATA statement" OptFull "Full or empty" OptTemporary "TEMPORARY or empty" Order "ORDER BY clause optional collation specification" OrderBy "ORDER BY clause" OrReplace "or replace" ByItem "BY item" OrderByOptional "Optional ORDER BY clause optional" ByList "BY list" AlterOrderItem "Alter Order item" AlterOrderList "Alter Order list" QuickOptional "QUICK or empty" QueryBlockOpt "Query block identifier optional" PartitionDefinition "Partition definition" PartitionDefinitionList "Partition definition list" PartitionDefinitionListOpt "Partition definition list option" PartitionKeyAlgorithmOpt "ALGORITHM = n option for KEY partition" PartitionMethod "Partition method" PartitionOpt "Partition option" PartitionNameList "Partition name list" PartitionNameListOpt "table partition names list optional" PartitionNumOpt "PARTITION NUM option" PartDefValuesOpt "VALUES {LESS THAN {(expr | value_list) | MAXVALUE} | IN {value_list}" PartDefOptionList "PartDefOption list" PartDefOption "COMMENT [=] xxx | TABLESPACE [=] tablespace_name | ENGINE [=] xxx" PasswordExpire "Single password option for create user statement" PasswordOpt "Password option" PasswordOrLockOption "Single password or lock option for create user statement" PasswordOrLockOptionList "Password or lock options for create user statement" PasswordOrLockOptions "Optional password or lock options for create user statement" ColumnPosition "Column position [First|After ColumnName]" PrepareSQL "Prepare statement sql string" PriorityOpt "Statement priority option" PrivElem "Privilege element" PrivElemList "Privilege element list" PrivLevel "Privilege scope" PrivType "Privilege type" ReferDef "Reference definition" OnDelete "ON DELETE clause" OnUpdate "ON UPDATE clause" OnDeleteUpdateOpt "optional ON DELETE and UPDATE clause" OptGConcatSeparator "optional GROUP_CONCAT SEPARATOR" ReferOpt "reference option" ReorganizePartitionRuleOpt "optional reorganize partition partition list and definitions" RequireList "require list" RequireListElement "require list element" Rolename "Rolename" RolenameList "RolenameList" RoleSpec "Rolename and auth option" RoleSpecList "Rolename and auth option list" RoleNameString "role name string" RowFormat "Row format option" RowValue "Row value" SelectLockOpt "FOR UPDATE or LOCK IN SHARE MODE," SelectStmtCalcFoundRows "SELECT statement optional SQL_CALC_FOUND_ROWS" SelectStmtSQLBigResult "SELECT statement optional SQL_BIG_RESULT" SelectStmtSQLBufferResult "SELECT statement optional SQL_BUFFER_RESULT" SelectStmtSQLCache "SELECT statement optional SQL_CAHCE/SQL_NO_CACHE" SelectStmtSQLSmallResult "SELECT statement optional SQL_SMALL_RESULT" SelectStmtStraightJoin "SELECT statement optional STRAIGHT_JOIN" SelectStmtFieldList "SELECT statement field list" SelectStmtLimit "SELECT statement optional LIMIT clause" SelectStmtOpts "Select statement options" SelectStmtBasic "SELECT statement from constant value" SelectStmtFromDualTable "SELECT statement from dual table" SelectStmtFromTable "SELECT statement from table" SelectStmtGroup "SELECT statement optional GROUP BY clause" SetRoleOpt "Set role options" SetDefaultRoleOpt "Set default role options" ShowTargetFilterable "Show target that can be filtered by WHERE or LIKE" ShowDatabaseNameOpt "Show tables/columns statement database name option" ShowTableAliasOpt "Show table alias option" ShowLikeOrWhereOpt "Show like or where clause option" ShowProfileArgsOpt "Show profile args option" ShowProfileTypesOpt "Show profile types option" ShowProfileType "Show profile type" ShowProfileTypes "Show profile types" SplitOption "Split Option" SplitSyntaxOption "Split syntax Option" Starting "Starting by" StatementList "statement list" StatsPersistentVal "stats_persistent value" StringName "string literal or identifier" StringList "string list" SubPartDefinition "SubPartition definition" SubPartDefinitionList "SubPartition definition list" SubPartDefinitionListOpt "SubPartition definition list optional" SubPartitionMethod "SubPartition method" SubPartitionOpt "SubPartition option" SubPartitionNumOpt "SubPartition NUM option" Symbol "Constraint Symbol" TableAsName "table alias name" TableAsNameOpt "table alias name optional" TableElement "table definition element" TableElementList "table definition element list" TableElementListOpt "table definition element list optional" TableFactor "table factor" TableLock "Table name and lock type" TableLockList "Table lock list" TableName "Table name" TableNameList "Table name list" TableNameListOpt "Table name list opt" TableOption "create table option" TableOptionList "create table option list" TableRef "table reference" TableRefs "table references" TableToTable "rename table to table" TableToTableList "rename table to table by list" TimeUnit "Time unit for 'DATE_ADD', 'DATE_SUB', 'ADDDATE', 'SUBDATE', 'EXTRACT'" TimestampUnit "Time unit for 'TIMESTAMPADD' and 'TIMESTAMPDIFF'" LockType "Table locks type" TransactionChar "Transaction characteristic" TransactionChars "Transaction characteristic list" TrimDirection "Trim string direction" UnionOpt "Union Option(empty/ALL/DISTINCT)" UnionClauseList "Union select clause list" UnionSelect "Union (select) item" Username "Username" UsernameList "UsernameList" UserSpec "Username and auth option" UserSpecList "Username and auth option list" UserVariableList "User defined variable name list" UsingRoles "UsingRoles is role option for SHOW GRANT" Values "values" ValuesList "values list" ValuesOpt "values optional" VariableAssignment "set variable value" VariableAssignmentList "set variable value list" ViewAlgorithm "view algorithm" ViewCheckOption "view check option" ViewDefiner "view definer" ViewName "view name" ViewFieldList "create view statement field list" ViewSQLSecurity "view sql security" WhereClause "WHERE clause" WhereClauseOptional "Optional WHERE clause" WhenClause "When clause" WhenClauseList "When clause list" WithReadLockOpt "With Read Lock opt" WithGrantOptionOpt "With Grant Option opt" WithValidation "with validation" WithValidationOpt "optional with validation" ElseOpt "Optional else clause" Type "Types" OptExistingWindowName "Optional existing WINDOW name" OptFromFirstLast "Optional FROM FIRST/LAST" OptLLDefault "Optional LEAD/LAG default" OptLeadLagInfo "Optional LEAD/LAG info" OptNullTreatment "Optional NULL treatment" OptPartitionClause "Optional PARTITION clause" OptWindowOrderByClause "Optional ORDER BY clause in WINDOW" OptWindowFrameClause "Optional FRAME clause in WINDOW" OptWindowingClause "Optional OVER clause" WindowingClause "OVER clause" WindowClauseOptional "Optional WINDOW clause" WindowDefinitionList "WINDOW definition list" WindowDefinition "WINDOW definition" WindowFrameUnits "WINDOW frame units" WindowFrameBetween "WINDOW frame between" WindowFrameBound "WINDOW frame bound" WindowFrameExtent "WINDOW frame extent" WindowFrameStart "WINDOW frame start" WindowFuncCall "WINDOW function call" WindowName "WINDOW name" WindowNameOrSpec "WINDOW name or spec" WindowSpec "WINDOW spec" WindowSpecDetails "WINDOW spec details" BetweenOrNotOp "Between predicate" IsOrNotOp "Is predicate" InOrNotOp "In predicate" LikeOrNotOp "Like predicate" RegexpOrNotOp "Regexp predicate" NumericType "Numeric types" IntegerType "Integer Types types" BooleanType "Boolean Types types" FixedPointType "Exact value types" FloatingPointType "Approximate value types" BitValueType "bit value types" StringType "String types" BlobType "Blob types" TextType "Text types" DateAndTimeType "Date and Time types" OptFieldLen "Field length or empty" FieldLen "Field length" FieldOpts "Field type definition option list" FieldOpt "Field type definition option" FloatOpt "Floating-point type option" Precision "Floating-point precision option" OptBinary "Optional BINARY" OptBinMod "Optional BINARY mode" OptCharsetWithOptBinary "Optional BINARY or ASCII or UNICODE or BYTE" OptCharset "Optional Character setting" OptCollate "Optional Collate setting" IgnoreLines "Ignore num(int) lines" NUM "A number" NumList "Some numbers" LengthNum "Field length num(uint64)" StorageOptimizerHintOpt "Storage level optimizer hint" TableOptimizerHintOpt "Table level optimizer hint" TableOptimizerHints "Table level optimizer hints" OptimizerHintList "optimizer hint list" HintTable "Table in optimizer hint" HintTableList "Table list in optimizer hint" HintStorageType "storage type in optimizer hint" HintStorageTypeAndTable "storage type and tables in optimizer hint" HintStorageTypeAndTableList "storage type and tables list in optimizer hint" HintTrueOrFalse "True or false in optimizer hint" HintQueryType "Query type in optimizer hint" HintMemoryQuota "Memory quota in optimizer hint" EnforcedOrNot "{ENFORCED|NOT ENFORCED}" EnforcedOrNotOpt "Optional {ENFORCED|NOT ENFORCED}" EnforcedOrNotOrNotNullOpt "{[ENFORCED|NOT ENFORCED|NOT NULL]}" Match "[MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]" MatchOpt "optional MATCH clause" %type AsOpt "AS or EmptyString" KeyOrIndex "{KEY|INDEX}" ColumnKeywordOpt "Column keyword or empty" PrimaryOpt "Optional primary keyword" NowSym "CURRENT_TIMESTAMP/LOCALTIME/LOCALTIMESTAMP" NowSymFunc "CURRENT_TIMESTAMP/LOCALTIME/LOCALTIMESTAMP/NOW" DefaultKwdOpt "optional DEFAULT keyword" DatabaseSym "DATABASE or SCHEMA" ExplainSym "EXPLAIN or DESCRIBE or DESC" RegexpSym "REGEXP or RLIKE" IntoOpt "INTO or EmptyString" ValueSym "Value or Values" Char "{CHAR|CHARACTER}" NChar "{NCHAR|NATIONAL CHARACTER|NATIONAL CHAR}" Varchar "{VARCHAR|VARCHARACTER|CHARACTER VARYING|CHAR VARYING}" NVarchar "{NATIONAL VARCHAR|NATIONAL VARCHARACTER|NVARCHAR|NCHAR VARCHAR|NATIONAL CHARACTER VARYING|NATIONAL CHAR VARYING|NCHAR VARYING}" Year "{YEAR|SQL_TSI_YEAR}" DeallocateSym "Deallocate or drop" OuterOpt "optional OUTER clause" CrossOpt "Cross join option" TablesTerminalSym "{TABLE|TABLES}" IsolationLevel "Isolation level" ShowIndexKwd "Show index/indexs/key keyword" DistinctKwd "DISTINCT/DISTINCTROW keyword" FromOrIn "From or In" OptTable "Optional table keyword" OptInteger "Optional Integer keyword" CharsetKw "charset or charater set" CommaOpt "optional comma" logAnd "logical and operator" logOr "logical or operator" LinearOpt "linear or empty" FieldsOrColumns "Fields or columns" StorageMedia "{DISK|MEMORY|DEFAULT}" %type ODBCDateTimeType "ODBC type keywords for date and time literals" Identifier "identifier or unreserved keyword" NotKeywordToken "Tokens not mysql keyword but treated specially" UnReservedKeyword "MySQL unreserved keywords" TiDBKeyword "TiDB added keywords" FunctionNameConflict "Built-in function call names which are conflict with keywords" FunctionNameOptionalBraces "Function with optional braces, all of them are reserved keywords." FunctionNameDatetimePrecision "Function with optional datetime precision, all of them are reserved keywords." FunctionNameDateArith "Date arith function call names (date_add or date_sub)" FunctionNameDateArithMultiForms "Date arith function call names (adddate or subdate)" VariableName "A simple Identifier like xx or the xx.xx form" %precedence empty %precedence sqlBufferResult %precedence sqlBigResult %precedence sqlSmallResult %precedence sqlCache sqlNoCache %precedence lowerThanIntervalKeyword %precedence interval %precedence lowerThanStringLitToken %precedence stringLit %precedence lowerThanSetKeyword %precedence set %precedence lowerThanInsertValues %precedence insertValues %precedence lowerThanCreateTableSelect %precedence createTableSelect %precedence lowerThanCharsetKwd %precedence charsetKwd %precedence lowerThanKey %precedence key %precedence lowerThanLocal %precedence local %precedence lowerThanRemove %precedence remove %precedence lowerThenOrder %precedence order %left join straightJoin inner cross left right full natural /* A dummy token to force the priority of TableRef production in a join. */ %left tableRefPriority %precedence lowerThanOn %precedence on using %right assignmentEq %left pipes or pipesAsOr %left xor %left andand and %left between %precedence lowerThanEq %left eq ge le neq neqSynonym '>' '<' is like in %left '|' %left '&' %left rsh lsh %left '-' '+' %left '*' '/' '%' div mod %left '^' %left '~' neg %precedence lowerThanNot %right not not2 %right collate %right encryption %left labels %precedence '(' %precedence quick %precedence escape %precedence lowerThanComma %precedence ',' %precedence higherThanComma %start Start %% Start: StatementList /**************************************AlterTableStmt*************************************** * See https://dev.mysql.com/doc/refman/5.7/en/alter-table.html *******************************************************************************************/ AlterTableStmt: "ALTER" IgnoreOptional "TABLE" TableName AlterTableSpecListOpt AlterTablePartitionOpt { specs := $5.([]*ast.AlterTableSpec) if $6 != nil { specs = append(specs, $6.(*ast.AlterTableSpec)) } $$ = &ast.AlterTableStmt{ Table: $4.(*ast.TableName), Specs: specs, } } | "ALTER" IgnoreOptional "TABLE" TableName "ANALYZE" "PARTITION" PartitionNameList AnalyzeOptionListOpt { $$ = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{$4.(*ast.TableName)}, PartitionNames: $7.([]model.CIStr), AnalyzeOpts: $8.([]ast.AnalyzeOpt),} } | "ALTER" IgnoreOptional "TABLE" TableName "ANALYZE" "PARTITION" PartitionNameList "INDEX" IndexNameList AnalyzeOptionListOpt { $$ = &ast.AnalyzeTableStmt{ TableNames: []*ast.TableName{$4.(*ast.TableName)}, PartitionNames: $7.([]model.CIStr), IndexNames: $9.([]model.CIStr), IndexFlag: true, AnalyzeOpts: $10.([]ast.AnalyzeOpt), } } AlterTablePartitionOpt: PartitionOpt { if $1 != nil { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTablePartition, Partition: $1.(*ast.PartitionOptions), } } else { $$ = nil } } | "REMOVE" "PARTITIONING" { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableRemovePartitioning, } yylex.AppendError(yylex.Errorf("The REMOVE PARTITIONING clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } LocationLabelList: { $$ = []string{} } | "LOCATION" "LABELS" StringList { $$ = $3 } AlterTableSpec: TableOptionList %prec higherThanComma { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableOption, Options:$1.([]*ast.TableOption), } } | "SET" "TIFLASH" "REPLICA" LengthNum LocationLabelList { tiflashReplicaSpec := &ast.TiFlashReplicaSpec{ Count: $4.(uint64), Labels: $5.([]string), } $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableSetTiFlashReplica, TiFlashReplica: tiflashReplicaSpec, } } | "CONVERT" "TO" CharsetKw CharsetName OptCollate { op := &ast.AlterTableSpec{ Tp: ast.AlterTableOption, Options:[]*ast.TableOption{{Tp: ast.TableOptionCharset, StrValue: $4.(string)}}, } if $5 != "" { op.Options = append(op.Options, &ast.TableOption{Tp: ast.TableOptionCollate, StrValue: $5.(string)}) } $$ = op } | "ADD" ColumnKeywordOpt IfNotExists ColumnDef ColumnPosition { $$ = &ast.AlterTableSpec{ IfNotExists: $3.(bool), Tp: ast.AlterTableAddColumns, NewColumns: []*ast.ColumnDef{$4.(*ast.ColumnDef)}, Position: $5.(*ast.ColumnPosition), } } | "ADD" ColumnKeywordOpt IfNotExists '(' TableElementList ')' { tes := $5.([]interface {}) var columnDefs []*ast.ColumnDef var constraints []*ast.Constraint for _, te := range tes { switch te := te.(type) { case *ast.ColumnDef: columnDefs = append(columnDefs, te) case *ast.Constraint: constraints = append(constraints, te) } } $$ = &ast.AlterTableSpec{ IfNotExists: $3.(bool), Tp: ast.AlterTableAddColumns, NewColumns: columnDefs, NewConstraints: constraints, } } | "ADD" Constraint { constraint := $2.(*ast.Constraint) $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableAddConstraint, Constraint: constraint, } } | "ADD" "PARTITION" IfNotExists NoWriteToBinLogAliasOpt PartitionDefinitionListOpt { var defs []*ast.PartitionDefinition if $5 != nil { defs = $5.([]*ast.PartitionDefinition) } noWriteToBinlog := $4.(bool) if noWriteToBinlog { yylex.AppendError(yylex.Errorf("The NO_WRITE_TO_BINLOG option is parsed but ignored for now.")) parser.lastErrorAsWarn() } $$ = &ast.AlterTableSpec{ IfNotExists: $3.(bool), NoWriteToBinlog: noWriteToBinlog, Tp: ast.AlterTableAddPartitions, PartDefinitions: defs, } } | "ADD" "PARTITION" IfNotExists NoWriteToBinLogAliasOpt "PARTITIONS" NUM { noWriteToBinlog := $4.(bool) if noWriteToBinlog { yylex.AppendError(yylex.Errorf("The NO_WRITE_TO_BINLOG option is parsed but ignored for now.")) parser.lastErrorAsWarn() } $$ = &ast.AlterTableSpec{ IfNotExists: $3.(bool), NoWriteToBinlog: noWriteToBinlog, Tp: ast.AlterTableAddPartitions, Num: getUint64FromNUM($6), } } | "CHECK" "PARTITION" AllOrPartitionNameList { yylex.AppendError(yylex.Errorf("The CHECK PARTITIONING clause is parsed but not implement yet.")) parser.lastErrorAsWarn() ret := &ast.AlterTableSpec{ Tp: ast.AlterTableCheckPartitions, } if $3 == nil { ret.OnAllPartitions = true } else { ret.PartitionNames = $3.([]model.CIStr) } $$ = ret } | "COALESCE" "PARTITION" NoWriteToBinLogAliasOpt NUM { noWriteToBinlog := $3.(bool) if noWriteToBinlog { yylex.AppendError(yylex.Errorf("The NO_WRITE_TO_BINLOG option is parsed but ignored for now.")) parser.lastErrorAsWarn() } $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableCoalescePartitions, NoWriteToBinlog: noWriteToBinlog, Num: getUint64FromNUM($4), } } | "DROP" ColumnKeywordOpt IfExists ColumnName RestrictOrCascadeOpt { $$ = &ast.AlterTableSpec{ IfExists: $3.(bool), Tp: ast.AlterTableDropColumn, OldColumnName: $4.(*ast.ColumnName), } } | "DROP" "PRIMARY" "KEY" { $$ = &ast.AlterTableSpec{Tp: ast.AlterTableDropPrimaryKey} } | "DROP" "PARTITION" IfExists PartitionNameList %prec lowerThanComma { $$ = &ast.AlterTableSpec{ IfExists: $3.(bool), Tp: ast.AlterTableDropPartition, PartitionNames: $4.([]model.CIStr), } } | "EXCHANGE" "PARTITION" Identifier "WITH" "TABLE" TableName WithValidationOpt { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableExchangePartition, PartitionNames: []model.CIStr{model.NewCIStr($3)}, NewTable: $6.(*ast.TableName), WithValidation: $7.(bool), } yylex.AppendError(yylex.Errorf("TiDB does not support EXCHANGE PARTITION now, it would be parsed but ignored.")) parser.lastErrorAsWarn() } | "TRUNCATE" "PARTITION" AllOrPartitionNameList { ret := &ast.AlterTableSpec{ Tp: ast.AlterTableTruncatePartition, } if $3 == nil { ret.OnAllPartitions = true yylex.AppendError(yylex.Errorf("The TRUNCATE PARTITION ALL clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } else { ret.PartitionNames = $3.([]model.CIStr) } $$ = ret } | "OPTIMIZE" "PARTITION" NoWriteToBinLogAliasOpt AllOrPartitionNameList { ret := &ast.AlterTableSpec{ NoWriteToBinlog: $3.(bool), Tp: ast.AlterTableOptimizePartition, } if $4 == nil { ret.OnAllPartitions = true } else { ret.PartitionNames = $4.([]model.CIStr) } $$ = ret yylex.AppendError(yylex.Errorf("The OPTIMIZE PARTITION clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } | "REPAIR" "PARTITION" NoWriteToBinLogAliasOpt AllOrPartitionNameList { ret := &ast.AlterTableSpec{ NoWriteToBinlog: $3.(bool), Tp: ast.AlterTableRepairPartition, } if $4 == nil { ret.OnAllPartitions = true } else { ret.PartitionNames = $4.([]model.CIStr) } $$ = ret yylex.AppendError(yylex.Errorf("The REPAIR PARTITION clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } | "IMPORT" "PARTITION" AllOrPartitionNameList "TABLESPACE" { ret := &ast.AlterTableSpec{ Tp: ast.AlterTableImportPartitionTablespace, } if $3 == nil { ret.OnAllPartitions = true } else { ret.PartitionNames = $3.([]model.CIStr) } $$ = ret yylex.AppendError(yylex.Errorf("The IMPORT PARTITION TABLESPACE clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } | "DISCARD" "PARTITION" AllOrPartitionNameList "TABLESPACE" { ret := &ast.AlterTableSpec{ Tp: ast.AlterTableDiscardPartitionTablespace, } if $3 == nil { ret.OnAllPartitions = true } else { ret.PartitionNames = $3.([]model.CIStr) } $$ = ret yylex.AppendError(yylex.Errorf("The DISCARD PARTITION TABLESPACE clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } | "IMPORT" "TABLESPACE" { ret := &ast.AlterTableSpec{ Tp: ast.AlterTableImportTablespace, } $$ = ret yylex.AppendError(yylex.Errorf("The IMPORT TABLESPACE clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } | "DISCARD" "TABLESPACE" { ret := &ast.AlterTableSpec{ Tp: ast.AlterTableDiscardTablespace, } $$ = ret yylex.AppendError(yylex.Errorf("The DISCARD TABLESPACE clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } | "REBUILD" "PARTITION" NoWriteToBinLogAliasOpt AllOrPartitionNameList { ret := &ast.AlterTableSpec{ Tp: ast.AlterTableRebuildPartition, NoWriteToBinlog: $3.(bool), } if $4 == nil { ret.OnAllPartitions = true } else { ret.PartitionNames = $4.([]model.CIStr) } $$ = ret yylex.AppendError(yylex.Errorf("REBUILD PARTITION syntax is parsed but not implement for now.")) parser.lastErrorAsWarn() } | "REORGANIZE" "PARTITION" NoWriteToBinLogAliasOpt ReorganizePartitionRuleOpt { ret := $4.(*ast.AlterTableSpec) ret.NoWriteToBinlog = $3.(bool) $$ = ret yylex.AppendError(yylex.Errorf("REORGANIZE PARTITION syntax is parsed but not implement for now.")) parser.lastErrorAsWarn() } | "DROP" KeyOrIndex IfExists Identifier { $$ = &ast.AlterTableSpec{ IfExists: $3.(bool), Tp: ast.AlterTableDropIndex, Name: $4, } } | "DROP" "FOREIGN" "KEY" IfExists Symbol { $$ = &ast.AlterTableSpec{ IfExists: $4.(bool), Tp: ast.AlterTableDropForeignKey, Name: $5.(string), } } | "ORDER" "BY" AlterOrderList %prec lowerThenOrder { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableOrderByColumns, OrderByList: $3.([]*ast.AlterOrderItem), } } | "DISABLE" "KEYS" { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableDisableKeys, } } | "ENABLE" "KEYS" { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableEnableKeys, } } | "MODIFY" ColumnKeywordOpt IfExists ColumnDef ColumnPosition { $$ = &ast.AlterTableSpec{ IfExists: $3.(bool), Tp: ast.AlterTableModifyColumn, NewColumns: []*ast.ColumnDef{$4.(*ast.ColumnDef)}, Position: $5.(*ast.ColumnPosition), } } | "CHANGE" ColumnKeywordOpt IfExists ColumnName ColumnDef ColumnPosition { $$ = &ast.AlterTableSpec{ IfExists: $3.(bool), Tp: ast.AlterTableChangeColumn, OldColumnName: $4.(*ast.ColumnName), NewColumns: []*ast.ColumnDef{$5.(*ast.ColumnDef)}, Position: $6.(*ast.ColumnPosition), } } | "ALTER" ColumnKeywordOpt ColumnName "SET" "DEFAULT" SignedLiteral { option := &ast.ColumnOption{Expr: $6} colDef := &ast.ColumnDef{ Name: $3.(*ast.ColumnName), Options: []*ast.ColumnOption{option}, } $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableAlterColumn, NewColumns: []*ast.ColumnDef{colDef}, } } | "ALTER" ColumnKeywordOpt ColumnName "SET" "DEFAULT" '(' Expression ')' { option := &ast.ColumnOption{Expr: $7} colDef := &ast.ColumnDef{ Name: $3.(*ast.ColumnName), Options: []*ast.ColumnOption{option}, } $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableAlterColumn, NewColumns: []*ast.ColumnDef{colDef}, } } | "ALTER" ColumnKeywordOpt ColumnName "DROP" "DEFAULT" { colDef := &ast.ColumnDef{ Name: $3.(*ast.ColumnName), } $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableAlterColumn, NewColumns: []*ast.ColumnDef{colDef}, } } | "RENAME" "COLUMN" ColumnName "TO" ColumnName { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableRenameColumn, OldColumnName: $3.(*ast.ColumnName), NewColumnName: $5.(*ast.ColumnName), } } | "RENAME" "TO" TableName { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableRenameTable, NewTable: $3.(*ast.TableName), } } | "RENAME" EqOpt TableName { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableRenameTable, NewTable: $3.(*ast.TableName), } } | "RENAME" "AS" TableName { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableRenameTable, NewTable: $3.(*ast.TableName), } } | "RENAME" KeyOrIndex Identifier "TO" Identifier { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableRenameIndex, FromKey: model.NewCIStr($3), ToKey: model.NewCIStr($5), } } | LockClause { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableLock, LockType: $1.(ast.LockType), } } | AlgorithmClause { // Parse it and ignore it. Just for compatibility. $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableAlgorithm, Algorithm: $1.(ast.AlgorithmType), } } | "FORCE" { // Parse it and ignore it. Just for compatibility. $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableForce, } } | "WITH" "VALIDATION" { // Parse it and ignore it. Just for compatibility. $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableWithValidation, } yylex.AppendError(yylex.Errorf("The WITH/WITHOUT VALIDATION clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } | "WITHOUT" "VALIDATION" { // Parse it and ignore it. Just for compatibility. $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableWithoutValidation, } yylex.AppendError(yylex.Errorf("The WITH/WITHOUT VALIDATION clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } // Added in MySQL 8.0.13, see: https://dev.mysql.com/doc/refman/8.0/en/keywords.html for details | "SECONDARY_LOAD" { // Parse it and ignore it. Just for compatibility. $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableSecondaryLoad, } yylex.AppendError(yylex.Errorf("The SECONDARY_LOAD clause is parsed but not implement yet.")) parser.lastErrorAsWarn() } // Added in MySQL 8.0.13, see: https://dev.mysql.com/doc/refman/8.0/en/keywords.html for details | "SECONDARY_UNLOAD" { // Parse it and ignore it. Just for compatibility. $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableSecondaryUnload, } yylex.AppendError(yylex.Errorf("The SECONDARY_UNLOAD VALIDATION clause is parsed but not implement yet.")) parser.lastErrorAsWarn() } | "ALTER" "CHECK" Identifier EnforcedOrNot { // Parse it and ignore it. Just for compatibility. c := &ast.Constraint{ Name: $3, Enforced: $4.(bool), } $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableAlterCheck, Constraint: c, } yylex.AppendError(yylex.Errorf("The ALTER CHECK clause is parsed but not implemented yet.")) parser.lastErrorAsWarn() } | "DROP" "CHECK" Identifier { // Parse it and ignore it. Just for compatibility. c := &ast.Constraint{ Name: $3, } $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableDropCheck, Constraint: c, } yylex.AppendError(yylex.Errorf("The DROP CHECK clause is parsed but not implemented yet.")) parser.lastErrorAsWarn() } | "ALTER" "INDEX" Identifier IndexInvisible { $$ = &ast.AlterTableSpec{ Tp: ast.AlterTableIndexInvisible, Name: $3, Visibility: $4.(ast.IndexVisibility), } } ReorganizePartitionRuleOpt: /* empty */ %prec lowerThanRemove { ret := &ast.AlterTableSpec{ Tp: ast.AlterTableReorganizePartition, OnAllPartitions: true, } $$ = ret } | PartitionNameList "INTO" '(' PartitionDefinitionList ')' { ret := &ast.AlterTableSpec{ Tp: ast.AlterTableReorganizePartition, PartitionNames: $1.([]model.CIStr), PartDefinitions: $4.([]*ast.PartitionDefinition), } $$ = ret } AllOrPartitionNameList: "ALL" { $$ = nil } | PartitionNameList %prec lowerThanComma { $$ = $1 } WithValidationOpt: { $$ = true } | WithValidation { $$ = $1 } WithValidation: "WITH" "VALIDATION" { $$ = true } | "WITHOUT" "VALIDATION" { $$ = false } AlgorithmClause: "ALGORITHM" EqOpt "DEFAULT" { $$ = ast.AlgorithmTypeDefault } | "ALGORITHM" EqOpt "COPY" { $$ = ast.AlgorithmTypeCopy } | "ALGORITHM" EqOpt "INPLACE" { $$ = ast.AlgorithmTypeInplace } | "ALGORITHM" EqOpt "INSTANT" { $$ = ast.AlgorithmTypeInstant } | "ALGORITHM" EqOpt identifier { yylex.AppendError(ErrUnknownAlterAlgorithm.GenWithStackByArgs($1)) return 1 } LockClause: "LOCK" EqOpt "DEFAULT" { $$ = ast.LockTypeDefault } | "LOCK" EqOpt Identifier { id := strings.ToUpper($3) if id == "NONE" { $$ = ast.LockTypeNone } else if id == "SHARED" { $$ = ast.LockTypeShared } else if id == "EXCLUSIVE" { $$ = ast.LockTypeExclusive } else { yylex.AppendError(ErrUnknownAlterLock.GenWithStackByArgs($3)) return 1 } } KeyOrIndex: "KEY" | "INDEX" KeyOrIndexOpt: {} | KeyOrIndex ColumnKeywordOpt: {} | "COLUMN" ColumnPosition: { $$ = &ast.ColumnPosition{Tp: ast.ColumnPositionNone} } | "FIRST" { $$ = &ast.ColumnPosition{Tp: ast.ColumnPositionFirst} } | "AFTER" ColumnName { $$ = &ast.ColumnPosition{ Tp: ast.ColumnPositionAfter, RelativeColumn: $2.(*ast.ColumnName), } } AlterTableSpecListOpt: /* empty */ { $$ = make([]*ast.AlterTableSpec, 0, 1) } | AlterTableSpecList { $$ = $1 } AlterTableSpecList: AlterTableSpec { $$ = []*ast.AlterTableSpec{$1.(*ast.AlterTableSpec)} } | AlterTableSpecList ',' AlterTableSpec { $$ = append($1.([]*ast.AlterTableSpec), $3.(*ast.AlterTableSpec)) } PartitionNameList: Identifier { $$ = []model.CIStr{model.NewCIStr($1)} } | PartitionNameList ',' Identifier { $$ = append($1.([]model.CIStr), model.NewCIStr($3)) } ConstraintKeywordOpt: { $$ = nil } | "CONSTRAINT" { $$ = nil } | "CONSTRAINT" Symbol { $$ = $2.(string) } Symbol: Identifier { $$ = $1 } /**************************************RenameTableStmt*************************************** * See http://dev.mysql.com/doc/refman/5.7/en/rename-table.html * * TODO: refactor this when you are going to add full support for multiple schema changes. * Currently it is only useful for syncer which depends heavily on tidb parser to do some dirty work. *******************************************************************************************/ RenameTableStmt: "RENAME" "TABLE" TableToTableList { $$ = &ast.RenameTableStmt{ OldTable: $3.([]*ast.TableToTable)[0].OldTable, NewTable: $3.([]*ast.TableToTable)[0].NewTable, TableToTables: $3.([]*ast.TableToTable), } } TableToTableList: TableToTable { $$ = []*ast.TableToTable{$1.(*ast.TableToTable)} } | TableToTableList ',' TableToTable { $$ = append($1.([]*ast.TableToTable), $3.(*ast.TableToTable)) } TableToTable: TableName "TO" TableName { $$ = &ast.TableToTable{ OldTable: $1.(*ast.TableName), NewTable: $3.(*ast.TableName), } } /******************************************************************* * * Recover Table Statement * * Example: * RECOVER TABLE t1; * RECOVER TABLE BY JOB 100; * *******************************************************************/ RecoverTableStmt: "RECOVER" "TABLE" "BY" "JOB" NUM { $$ = &ast.RecoverTableStmt{ JobID: $5.(int64), } } | "RECOVER" "TABLE" TableName { $$ = &ast.RecoverTableStmt{ Table: $3.(*ast.TableName), } } | "RECOVER" "TABLE" TableName NUM { $$ = &ast.RecoverTableStmt{ Table: $3.(*ast.TableName), JobNum: $4.(int64), } } /******************************************************************* * * Split index region statement * * Example: * SPLIT TABLE table_name INDEX index_name BY (val1...),(val2...)... * *******************************************************************/ SplitRegionStmt: "SPLIT" SplitSyntaxOption "TABLE" TableName PartitionNameListOpt SplitOption { $$ = &ast.SplitRegionStmt{ SplitSyntaxOpt: $2.(*ast.SplitSyntaxOption), Table: $4.(*ast.TableName), PartitionNames: $5.([]model.CIStr), SplitOpt: $6.(*ast.SplitOption), } } | "SPLIT" SplitSyntaxOption "TABLE" TableName PartitionNameListOpt "INDEX" Identifier SplitOption { $$ = &ast.SplitRegionStmt{ SplitSyntaxOpt: $2.(*ast.SplitSyntaxOption), Table: $4.(*ast.TableName), PartitionNames: $5.([]model.CIStr), IndexName: model.NewCIStr($7), SplitOpt: $8.(*ast.SplitOption), } } SplitOption: "BETWEEN" RowValue "AND" RowValue "REGIONS" NUM { $$ = &ast.SplitOption{ Lower: $2.([]ast.ExprNode), Upper: $4.([]ast.ExprNode), Num: $6.(int64), } } | "BY" ValuesList { $$ = &ast.SplitOption{ ValueLists: $2.([][]ast.ExprNode), } } SplitSyntaxOption: /* empty */ { $$ = &ast.SplitSyntaxOption{} } | "REGION" "FOR" { $$ = &ast.SplitSyntaxOption{ HasRegionFor: true, } } | "PARTITION" { $$ = &ast.SplitSyntaxOption{ HasPartition: true, } } | "REGION" "FOR" "PARTITION" { $$ = &ast.SplitSyntaxOption{ HasRegionFor: true, HasPartition: true, } } /*******************************************************************************************/ AnalyzeTableStmt: "ANALYZE" "TABLE" TableNameList AnalyzeOptionListOpt { $$ = &ast.AnalyzeTableStmt{TableNames: $3.([]*ast.TableName), AnalyzeOpts: $4.([]ast.AnalyzeOpt),} } | "ANALYZE" "TABLE" TableName "INDEX" IndexNameList AnalyzeOptionListOpt { $$ = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{$3.(*ast.TableName)}, IndexNames: $5.([]model.CIStr), IndexFlag: true, AnalyzeOpts: $6.([]ast.AnalyzeOpt),} } | "ANALYZE" "INCREMENTAL" "TABLE" TableName "INDEX" IndexNameList AnalyzeOptionListOpt { $$ = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{$4.(*ast.TableName)}, IndexNames: $6.([]model.CIStr), IndexFlag: true, Incremental: true, AnalyzeOpts: $7.([]ast.AnalyzeOpt),} } | "ANALYZE" "TABLE" TableName "PARTITION" PartitionNameList AnalyzeOptionListOpt { $$ = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{$3.(*ast.TableName)}, PartitionNames: $5.([]model.CIStr), AnalyzeOpts: $6.([]ast.AnalyzeOpt),} } | "ANALYZE" "TABLE" TableName "PARTITION" PartitionNameList "INDEX" IndexNameList AnalyzeOptionListOpt { $$ = &ast.AnalyzeTableStmt{ TableNames: []*ast.TableName{$3.(*ast.TableName)}, PartitionNames: $5.([]model.CIStr), IndexNames: $7.([]model.CIStr), IndexFlag: true, AnalyzeOpts: $8.([]ast.AnalyzeOpt), } } | "ANALYZE" "INCREMENTAL" "TABLE" TableName "PARTITION" PartitionNameList "INDEX" IndexNameList AnalyzeOptionListOpt { $$ = &ast.AnalyzeTableStmt{ TableNames: []*ast.TableName{$4.(*ast.TableName)}, PartitionNames: $6.([]model.CIStr), IndexNames: $8.([]model.CIStr), IndexFlag: true, Incremental: true, AnalyzeOpts: $9.([]ast.AnalyzeOpt), } } AnalyzeOptionListOpt: { $$ = []ast.AnalyzeOpt{} } | "WITH" AnalyzeOptionList { $$ = $2.([]ast.AnalyzeOpt) } AnalyzeOptionList: AnalyzeOption { $$ = []ast.AnalyzeOpt{$1.(ast.AnalyzeOpt)} } | AnalyzeOptionList ',' AnalyzeOption { $$ = append($1.([]ast.AnalyzeOpt), $3.(ast.AnalyzeOpt)) } AnalyzeOption: NUM "BUCKETS" { $$ = ast.AnalyzeOpt{Type: ast.AnalyzeOptNumBuckets, Value: getUint64FromNUM($1)} } | NUM "TOPN" { $$ = ast.AnalyzeOpt{Type: ast.AnalyzeOptNumTopN, Value: getUint64FromNUM($1)} } | NUM "CMSKETCH" "DEPTH" { $$ = ast.AnalyzeOpt{Type: ast.AnalyzeOptCMSketchDepth, Value: getUint64FromNUM($1)} } | NUM "CMSKETCH" "WIDTH" { $$ = ast.AnalyzeOpt{Type: ast.AnalyzeOptCMSketchWidth, Value: getUint64FromNUM($1)} } | NUM "SAMPLES" { $$ = ast.AnalyzeOpt{Type: ast.AnalyzeOptNumSamples, Value: getUint64FromNUM($1)} } /*******************************************************************************************/ Assignment: ColumnName eq ExprOrDefault { $$ = &ast.Assignment{Column: $1.(*ast.ColumnName), Expr:$3} } AssignmentList: Assignment { $$ = []*ast.Assignment{$1.(*ast.Assignment)} } | AssignmentList ',' Assignment { $$ = append($1.([]*ast.Assignment), $3.(*ast.Assignment)) } AssignmentListOpt: /* EMPTY */ { $$ = []*ast.Assignment{} } | AssignmentList BeginTransactionStmt: "BEGIN" { $$ = &ast.BeginStmt{} } | "BEGIN" "PESSIMISTIC" { $$ = &ast.BeginStmt{ Mode: ast.Pessimistic, } } | "BEGIN" "OPTIMISTIC" { $$ = &ast.BeginStmt{ Mode: ast.Optimistic, } } | "START" "TRANSACTION" { $$ = &ast.BeginStmt{} } | "START" "TRANSACTION" "WITH" "CONSISTENT" "SNAPSHOT" { $$ = &ast.BeginStmt{} } BinlogStmt: "BINLOG" stringLit { $$ = &ast.BinlogStmt{Str: $2} } ColumnDefList: ColumnDef { $$ = []*ast.ColumnDef{$1.(*ast.ColumnDef)} } | ColumnDefList ',' ColumnDef { $$ = append($1.([]*ast.ColumnDef), $3.(*ast.ColumnDef)) } ColumnDef: ColumnName Type ColumnOptionListOpt { colDef := &ast.ColumnDef{Name: $1.(*ast.ColumnName), Tp: $2.(*types.FieldType), Options: $3.([]*ast.ColumnOption)} if !colDef.Validate() { yylex.AppendError(yylex.Errorf("Invalid column definition")) return 1 } $$ = colDef } | ColumnName "SERIAL" ColumnOptionListOpt { // TODO: check flen 0 tp := types.NewFieldType(mysql.TypeLonglong) options := []*ast.ColumnOption{{Tp: ast.ColumnOptionNotNull}, {Tp: ast.ColumnOptionAutoIncrement}, {Tp: ast.ColumnOptionUniqKey}} options = append(options, $3.([]*ast.ColumnOption)...) tp.Flag |= mysql.UnsignedFlag colDef := &ast.ColumnDef{Name: $1.(*ast.ColumnName), Tp: tp, Options: options} if !colDef.Validate() { yylex.AppendError(yylex.Errorf("Invalid column definition")) return 1 } $$ = colDef } ColumnName: Identifier { $$ = &ast.ColumnName{Name: model.NewCIStr($1)} } | Identifier '.' Identifier { $$ = &ast.ColumnName{Table: model.NewCIStr($1), Name: model.NewCIStr($3)} } | Identifier '.' Identifier '.' Identifier { $$ = &ast.ColumnName{Schema: model.NewCIStr($1), Table: model.NewCIStr($3), Name: model.NewCIStr($5)} } ColumnNameList: ColumnName { $$ = []*ast.ColumnName{$1.(*ast.ColumnName)} } | ColumnNameList ',' ColumnName { $$ = append($1.([]*ast.ColumnName), $3.(*ast.ColumnName)) } ColumnNameListOpt: /* EMPTY */ { $$ = []*ast.ColumnName{} } | ColumnNameList { $$ = $1.([]*ast.ColumnName) } ColumnNameOrUserVarListOpt: /* EMPTY */ { $$ = []*ast.ColumnNameOrUserVar{} } | ColumnNameOrUserVariableList { $$ = $1.([]*ast.ColumnNameOrUserVar) } ColumnNameOrUserVariableList: ColumnNameOrUserVariable { $$ = []*ast.ColumnNameOrUserVar{$1.(*ast.ColumnNameOrUserVar)} } | ColumnNameOrUserVariableList ',' ColumnNameOrUserVariable { $$ = append($1.([]*ast.ColumnNameOrUserVar), $3.(*ast.ColumnNameOrUserVar)) } ColumnNameOrUserVariable: ColumnName { $$ = &ast.ColumnNameOrUserVar{ColumnName: $1.(*ast.ColumnName)} } | UserVariable { $$ = &ast.ColumnNameOrUserVar{UserVar: $1.(*ast.VariableExpr)} } ColumnNameOrUserVarListOptWithBrackets: /* EMPTY */ { $$ = []*ast.ColumnNameOrUserVar{} } | '(' ColumnNameOrUserVarListOpt ')' { $$ = $2.([]*ast.ColumnNameOrUserVar) } CommitStmt: "COMMIT" { $$ = &ast.CommitStmt{} } PrimaryOpt: {} | "PRIMARY" EnforcedOrNot: "ENFORCED" { $$ = true } | "NOT" "ENFORCED" { $$ = false } EnforcedOrNotOpt: { $$ = true } %prec lowerThanNot | EnforcedOrNot { $$ = $1 } EnforcedOrNotOrNotNullOpt: // This branch is needed to workaround the need of a lookahead of 2 for the grammar: // // { [NOT] NULL | CHECK(...) [NOT] ENFORCED } ... "NOT" "NULL" { $$ = 0 } | EnforcedOrNotOpt { if ($1.(bool)) { $$ = 1 } else { $$ = 2 } } ColumnOption: "NOT" "NULL" { $$ = &ast.ColumnOption{Tp: ast.ColumnOptionNotNull} } | "NULL" { $$ = &ast.ColumnOption{Tp: ast.ColumnOptionNull} } | "AUTO_INCREMENT" { $$ = &ast.ColumnOption{Tp: ast.ColumnOptionAutoIncrement} } | PrimaryOpt "KEY" { // KEY is normally a synonym for INDEX. The key attribute PRIMARY KEY // can also be specified as just KEY when given in a column definition. // See http://dev.mysql.com/doc/refman/5.7/en/create-table.html $$ = &ast.ColumnOption{Tp: ast.ColumnOptionPrimaryKey} } | "UNIQUE" %prec lowerThanKey { $$ = &ast.ColumnOption{Tp: ast.ColumnOptionUniqKey} } | "UNIQUE" "KEY" { $$ = &ast.ColumnOption{Tp: ast.ColumnOptionUniqKey} } | "DEFAULT" DefaultValueExpr { $$ = &ast.ColumnOption{Tp: ast.ColumnOptionDefaultValue, Expr: $2} } | "SERIAL" "DEFAULT" "VALUE" { $$ = []*ast.ColumnOption{{Tp: ast.ColumnOptionNotNull}, {Tp: ast.ColumnOptionAutoIncrement}, {Tp: ast.ColumnOptionUniqKey}} } | "ON" "UPDATE" NowSymOptionFraction { $$ = &ast.ColumnOption{Tp: ast.ColumnOptionOnUpdate, Expr: $3} } | "COMMENT" stringLit { $$ = &ast.ColumnOption{Tp: ast.ColumnOptionComment, Expr: ast.NewValueExpr($2)} } | ConstraintKeywordOpt "CHECK" '(' Expression ')' EnforcedOrNotOrNotNullOpt { // See https://dev.mysql.com/doc/refman/5.7/en/create-table.html // The CHECK clause is parsed but ignored by all storage engines. // See the branch named `EnforcedOrNotOrNotNullOpt`. optionCheck := &ast.ColumnOption{ Tp: ast.ColumnOptionCheck, Expr: $4, Enforced: true, } switch $6.(int) { case 0: $$ = []*ast.ColumnOption{optionCheck, {Tp: ast.ColumnOptionNotNull}} case 1: optionCheck.Enforced = true $$ = optionCheck case 2: optionCheck.Enforced = false $$ = optionCheck default: } yylex.AppendError(yylex.Errorf("The CHECK clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } | GeneratedAlways "AS" '(' Expression ')' VirtualOrStored { startOffset := parser.startOffset(&yyS[yypt-2]) endOffset := parser.endOffset(&yyS[yypt-1]) expr := $4 expr.SetText(parser.src[startOffset:endOffset]) $$ = &ast.ColumnOption{ Tp: ast.ColumnOptionGenerated, Expr: expr, Stored: $6.(bool), } } | ReferDef { $$ = &ast.ColumnOption{ Tp: ast.ColumnOptionReference, Refer: $1.(*ast.ReferenceDef), } } | "COLLATE" CollationName { $$ = &ast.ColumnOption{Tp: ast.ColumnOptionCollate, StrValue: $2.(string)} } | "COLUMN_FORMAT" ColumnFormat { $$ = &ast.ColumnOption{Tp: ast.ColumnOptionColumnFormat, StrValue: $2.(string)} } | "STORAGE" StorageMedia { $$ = &ast.ColumnOption{Tp: ast.ColumnOptionStorage, StrValue: $2} yylex.AppendError(yylex.Errorf("The STORAGE clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } StorageMedia: "DEFAULT" | "DISK" | "MEMORY" ColumnFormat: "DEFAULT" { $$ = "DEFAULT" } | "FIXED" { $$ = "FIXED" } | "DYNAMIC" { $$ = "DYNAMIC" } GeneratedAlways: | "GENERATED" "ALWAYS" VirtualOrStored: { $$ = false } | "VIRTUAL" { $$ = false } | "STORED" { $$ = true } ColumnOptionList: ColumnOption { if columnOption,ok := $1.(*ast.ColumnOption); ok { $$ = []*ast.ColumnOption{columnOption} } else { $$ = $1 } } | ColumnOptionList ColumnOption { if columnOption,ok := $2.(*ast.ColumnOption); ok { $$ = append($1.([]*ast.ColumnOption), columnOption) } else { $$ = append($1.([]*ast.ColumnOption), $2.([]*ast.ColumnOption)...) } } ColumnOptionListOpt: { $$ = []*ast.ColumnOption{} } | ColumnOptionList { $$ = $1.([]*ast.ColumnOption) } ConstraintElem: "PRIMARY" "KEY" IndexNameAndTypeOpt '(' IndexColNameList ')' IndexOptionList { c := &ast.Constraint{ Tp: ast.ConstraintPrimaryKey, Keys: $5.([]*ast.IndexColName), } if $7 != nil { c.Option = $7.(*ast.IndexOption) } if indexType := $3.([]interface{})[1]; indexType != nil { if c.Option == nil { c.Option = &ast.IndexOption{} } c.Option.Tp = indexType.(model.IndexType) } $$ = c } | "FULLTEXT" KeyOrIndexOpt IndexName '(' IndexColNameList ')' IndexOptionList { c := &ast.Constraint{ Tp: ast.ConstraintFulltext, Keys: $5.([]*ast.IndexColName), Name: $3.(string), } if $7 != nil { c.Option = $7.(*ast.IndexOption) } $$ = c } | KeyOrIndex IfNotExists IndexNameAndTypeOpt '(' IndexColNameList ')' IndexOptionList { c := &ast.Constraint{ IfNotExists: $2.(bool), Tp: ast.ConstraintIndex, Keys: $5.([]*ast.IndexColName), } if $7 != nil { c.Option = $7.(*ast.IndexOption) } c.Name = $3.([]interface{})[0].(string) if indexType := $3.([]interface{})[1]; indexType != nil { if c.Option == nil { c.Option = &ast.IndexOption{} } c.Option.Tp = indexType.(model.IndexType) } $$ = c } | "UNIQUE" KeyOrIndexOpt IndexNameAndTypeOpt '(' IndexColNameList ')' IndexOptionList { c := &ast.Constraint{ Tp: ast.ConstraintUniq, Keys: $5.([]*ast.IndexColName), } if $7 != nil { c.Option = $7.(*ast.IndexOption) } c.Name = $3.([]interface{})[0].(string) if indexType := $3.([]interface{})[1]; indexType != nil { if c.Option == nil { c.Option = &ast.IndexOption{} } c.Option.Tp = indexType.(model.IndexType) } $$ = c } | "FOREIGN" "KEY" IfNotExists IndexName '(' IndexColNameList ')' ReferDef { $$ = &ast.Constraint{ IfNotExists: $3.(bool), Tp: ast.ConstraintForeignKey, Keys: $6.([]*ast.IndexColName), Name: $4.(string), Refer: $8.(*ast.ReferenceDef), } } | "CHECK" '(' Expression ')' EnforcedOrNotOpt { $$ = &ast.Constraint{ Tp: ast.ConstraintCheck, Expr: $3.(ast.ExprNode), Enforced: $5.(bool), } yylex.AppendError(yylex.Errorf("The CHECK clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } Match: "MATCH" "FULL" { $$ = ast.MatchFull } | "MATCH" "PARTIAL" { $$ = ast.MatchPartial } | "MATCH" "SIMPLE" { $$ = ast.MatchSimple } MatchOpt: { $$ = ast.MatchNone } | Match { $$ = $1 yylex.AppendError(yylex.Errorf("The MATCH clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } ReferDef: "REFERENCES" TableName IndexColNameListOpt MatchOpt OnDeleteUpdateOpt { onDeleteUpdate := $5.([2]interface{}) $$ = &ast.ReferenceDef{ Table: $2.(*ast.TableName), IndexColNames: $3.([]*ast.IndexColName), OnDelete: onDeleteUpdate[0].(*ast.OnDeleteOpt), OnUpdate: onDeleteUpdate[1].(*ast.OnUpdateOpt), Match: $4.(ast.MatchType), } } IndexColNameListOpt: { $$ = ([]*ast.IndexColName)(nil) } | '(' IndexColNameList ')' { $$ = $2 } OnDelete: "ON" "DELETE" ReferOpt { $$ = &ast.OnDeleteOpt{ReferOpt: $3.(ast.ReferOptionType)} } OnUpdate: "ON" "UPDATE" ReferOpt { $$ = &ast.OnUpdateOpt{ReferOpt: $3.(ast.ReferOptionType)} } OnDeleteUpdateOpt: { $$ = [2]interface{}{&ast.OnDeleteOpt{}, &ast.OnUpdateOpt{}} } %prec lowerThanOn | OnDelete %prec lowerThanOn { $$ = [2]interface{}{$1, &ast.OnUpdateOpt{}} } | OnUpdate %prec lowerThanOn { $$ = [2]interface{}{&ast.OnDeleteOpt{}, $1} } | OnDelete OnUpdate { $$ = [2]interface{}{$1, $2} } | OnUpdate OnDelete { $$ = [2]interface{}{$2, $1} } ReferOpt: "RESTRICT" { $$ = ast.ReferOptionRestrict } | "CASCADE" { $$ = ast.ReferOptionCascade } | "SET" "NULL" { $$ = ast.ReferOptionSetNull } | "NO" "ACTION" { $$ = ast.ReferOptionNoAction } | "SET" "DEFAULT" { $$ = ast.ReferOptionSetDefault yylex.AppendError(yylex.Errorf("The SET DEFAULT clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } /* * The DEFAULT clause specifies a default value for a column. * With one exception, the default value must be a constant; * it cannot be a function or an expression. This means, for example, * that you cannot set the default for a date column to be the value of * a function such as NOW() or CURRENT_DATE. The exception is that you * can specify CURRENT_TIMESTAMP as the default for a TIMESTAMP or DATETIME column. * * See http://dev.mysql.com/doc/refman/5.7/en/create-table.html * https://github.com/mysql/mysql-server/blob/5.7/sql/sql_yacc.yy#L6832 */ DefaultValueExpr: NowSymOptionFraction | SignedLiteral NowSymOptionFraction: NowSym { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")} } | NowSymFunc '(' ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")} } | NowSymFunc '(' NUM ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP"), Args: []ast.ExprNode{ast.NewValueExpr($3)}} } /* * See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_localtime * TODO: Process other three keywords */ NowSymFunc: "CURRENT_TIMESTAMP" | "LOCALTIME" | "LOCALTIMESTAMP" | builtinNow NowSym: "CURRENT_TIMESTAMP" | "LOCALTIME" | "LOCALTIMESTAMP" SignedLiteral: Literal { $$ = ast.NewValueExpr($1) } | '+' NumLiteral { $$ = &ast.UnaryOperationExpr{Op: opcode.Plus, V: ast.NewValueExpr($2)} } | '-' NumLiteral { $$ = &ast.UnaryOperationExpr{Op: opcode.Minus, V: ast.NewValueExpr($2)} } NumLiteral: intLit | floatLit | decLit /**************************************CreateIndexStmt*************************************** * See https://dev.mysql.com/doc/refman/8.0/en/create-index.html * * TYPE type_name is recognized as a synonym for USING type_name. However, USING is the preferred form. * * CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name * [index_type] * ON tbl_name (key_part,...) * [index_option] * [algorithm_option | lock_option] ... * * key_part: {col_name [(length)] | (expr)} [ASC | DESC] * * index_option: * KEY_BLOCK_SIZE [=] value * | index_type * | WITH PARSER parser_name * | COMMENT 'string' * | {VISIBLE | INVISIBLE} * * index_type: * USING {BTREE | HASH} * * algorithm_option: * ALGORITHM [=] {DEFAULT | INPLACE | COPY} * * lock_option: * LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE} *******************************************************************************************/ CreateIndexStmt: "CREATE" IndexKeyTypeOpt "INDEX" IfNotExists Identifier IndexTypeOpt "ON" TableName '(' IndexColNameList ')' IndexOptionList IndexLockAndAlgorithmOpt { var indexOption *ast.IndexOption if $12 != nil { indexOption = $12.(*ast.IndexOption) if indexOption.Tp == model.IndexTypeInvalid { if $6 != nil { indexOption.Tp = $6.(model.IndexType) } } } else { indexOption = &ast.IndexOption{} if $6 != nil { indexOption.Tp = $6.(model.IndexType) } } var indexLockAndAlgorithm *ast.IndexLockAndAlgorithm if $13 != nil { indexLockAndAlgorithm = $13.(*ast.IndexLockAndAlgorithm) if indexLockAndAlgorithm.LockTp == ast.LockTypeDefault && indexLockAndAlgorithm.AlgorithmTp == ast.AlgorithmTypeDefault { indexLockAndAlgorithm = nil } } $$ = &ast.CreateIndexStmt{ IfNotExists: $4.(bool), IndexName: $5, Table: $8.(*ast.TableName), IndexColNames: $10.([]*ast.IndexColName), IndexOption: indexOption, KeyType: $2.(ast.IndexKeyType), LockAlg: indexLockAndAlgorithm, } } IndexColName: ColumnName OptFieldLen Order { //Order is parsed but just ignored as MySQL did $$ = &ast.IndexColName{Column: $1.(*ast.ColumnName), Length: $2.(int)} } IndexColNameList: IndexColName { $$ = []*ast.IndexColName{$1.(*ast.IndexColName)} } | IndexColNameList ',' IndexColName { $$ = append($1.([]*ast.IndexColName), $3.(*ast.IndexColName)) } IndexLockAndAlgorithmOpt: { $$ = nil } | LockClause { $$ = &ast.IndexLockAndAlgorithm{ LockTp: $1.(ast.LockType), AlgorithmTp: ast.AlgorithmTypeDefault, } } | AlgorithmClause { $$ = &ast.IndexLockAndAlgorithm{ LockTp: ast.LockTypeDefault, AlgorithmTp: $1.(ast.AlgorithmType), } } | LockClause AlgorithmClause { $$ = &ast.IndexLockAndAlgorithm{ LockTp: $1.(ast.LockType), AlgorithmTp: $2.(ast.AlgorithmType), } } | AlgorithmClause LockClause { $$ = &ast.IndexLockAndAlgorithm{ LockTp: $2.(ast.LockType), AlgorithmTp: $1.(ast.AlgorithmType), } } IndexKeyTypeOpt: { $$ = ast.IndexKeyTypeNone } | "UNIQUE" { $$ = ast.IndexKeyTypeUnique } | "SPATIAL" { $$ = ast.IndexKeyTypeSpatial } | "FULLTEXT" { $$ = ast.IndexKeyTypeFullText } /**************************************AlterDatabaseStmt*************************************** * See https://dev.mysql.com/doc/refman/5.7/en/alter-database.html * 'ALTER DATABASE ... UPGRADE DATA DIRECTORY NAME' is not supported yet. * * ALTER {DATABASE | SCHEMA} [db_name] * alter_specification ... * * alter_specification: * [DEFAULT] CHARACTER SET [=] charset_name * | [DEFAULT] COLLATE [=] collation_name * | [DEFAULT] ENCRYPTION [=] {'Y' | 'N'} *******************************************************************************************/ AlterDatabaseStmt: "ALTER" DatabaseSym DBName DatabaseOptionList { $$ = &ast.AlterDatabaseStmt{ Name: $3.(string), AlterDefaultDatabase: false, Options: $4.([]*ast.DatabaseOption), } } | "ALTER" DatabaseSym DatabaseOptionList { $$ = &ast.AlterDatabaseStmt{ Name: "", AlterDefaultDatabase: true, Options: $3.([]*ast.DatabaseOption), } } /******************************************************************* * * Create Database Statement * CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name * [create_specification] ... * * create_specification: * [DEFAULT] CHARACTER SET [=] charset_name * | [DEFAULT] COLLATE [=] collation_name * | [DEFAULT] ENCRYPTION [=] {'Y' | 'N'} *******************************************************************/ CreateDatabaseStmt: "CREATE" DatabaseSym IfNotExists DBName DatabaseOptionListOpt { $$ = &ast.CreateDatabaseStmt{ IfNotExists: $3.(bool), Name: $4.(string), Options: $5.([]*ast.DatabaseOption), } } DBName: Identifier { $$ = $1 } DatabaseOption: DefaultKwdOpt CharsetKw EqOpt CharsetName { $$ = &ast.DatabaseOption{Tp: ast.DatabaseOptionCharset, Value: $4.(string)} } | DefaultKwdOpt "COLLATE" EqOpt CollationName { $$ = &ast.DatabaseOption{Tp: ast.DatabaseOptionCollate, Value: $4.(string)} } | DefaultKwdOpt "ENCRYPTION" EqOpt stringLit { $$ = &ast.DatabaseOption{Tp: ast.DatabaseOptionEncryption, Value: $4} } DatabaseOptionListOpt: { $$ = []*ast.DatabaseOption{} } | DatabaseOptionList DatabaseOptionList: DatabaseOption { $$ = []*ast.DatabaseOption{$1.(*ast.DatabaseOption)} } | DatabaseOptionList DatabaseOption { $$ = append($1.([]*ast.DatabaseOption), $2.(*ast.DatabaseOption)) } /******************************************************************* * * Create Table Statement * * Example: * CREATE TABLE Persons * ( * P_Id int NOT NULL, * LastName varchar(255) NOT NULL, * FirstName varchar(255), * Address varchar(255), * City varchar(255), * PRIMARY KEY (P_Id) * ) *******************************************************************/ CreateTableStmt: "CREATE" OptTemporary "TABLE" IfNotExists TableName TableElementListOpt CreateTableOptionListOpt PartitionOpt DuplicateOpt AsOpt CreateTableSelectOpt { stmt := $6.(*ast.CreateTableStmt) stmt.Table = $5.(*ast.TableName) stmt.IfNotExists = $4.(bool) stmt.IsTemporary = $2.(bool) stmt.Options = $7.([]*ast.TableOption) if $8 != nil { stmt.Partition = $8.(*ast.PartitionOptions) } stmt.OnDuplicate = $9.(ast.OnDuplicateKeyHandlingType) stmt.Select = $11.(*ast.CreateTableStmt).Select $$ = stmt } | "CREATE" OptTemporary "TABLE" IfNotExists TableName LikeTableWithOrWithoutParen { $$ = &ast.CreateTableStmt{ Table: $5.(*ast.TableName), ReferTable: $6.(*ast.TableName), IfNotExists: $4.(bool), IsTemporary: $2.(bool), } } DefaultKwdOpt: %prec lowerThanCharsetKwd {} | "DEFAULT" PartitionOpt: { $$ = nil } | "PARTITION" "BY" PartitionMethod PartitionNumOpt SubPartitionOpt PartitionDefinitionListOpt { method := $3.(*ast.PartitionMethod) method.Num = $4.(uint64) sub, _ := $5.(*ast.PartitionMethod) defs, _ := $6.([]*ast.PartitionDefinition) opt := &ast.PartitionOptions{ PartitionMethod: *method, Sub: sub, Definitions: defs, } if err := opt.Validate(); err != nil { yylex.AppendError(err) return 1 } $$ = opt } SubPartitionMethod: LinearOpt "KEY" PartitionKeyAlgorithmOpt '(' ColumnNameListOpt ')' { $$ = &ast.PartitionMethod{ Tp: model.PartitionTypeKey, Linear: len($1) != 0, ColumnNames: $5.([]*ast.ColumnName), } } | LinearOpt "HASH" '(' Expression ')' { $$ = &ast.PartitionMethod{ Tp: model.PartitionTypeHash, Linear: len($1) != 0, Expr: $4.(ast.ExprNode), } } PartitionKeyAlgorithmOpt: /* empty */ {} | "ALGORITHM" '=' NUM {} PartitionMethod: SubPartitionMethod { $$ = $1 } | "RANGE" '(' Expression ')' { $$ = &ast.PartitionMethod{ Tp: model.PartitionTypeRange, Expr: $3.(ast.ExprNode), } } | "RANGE" FieldsOrColumns '(' ColumnNameList ')' { $$ = &ast.PartitionMethod{ Tp: model.PartitionTypeRange, ColumnNames: $4.([]*ast.ColumnName), } } | "LIST" '(' Expression ')' { $$ = &ast.PartitionMethod{ Tp: model.PartitionTypeList, Expr: $3.(ast.ExprNode), } } | "LIST" FieldsOrColumns '(' ColumnNameList ')' { $$ = &ast.PartitionMethod{ Tp: model.PartitionTypeList, ColumnNames: $4.([]*ast.ColumnName), } } | "SYSTEM_TIME" "INTERVAL" Expression TimeUnit { $$ = &ast.PartitionMethod{ Tp: model.PartitionTypeSystemTime, Expr: $3.(ast.ExprNode), Unit: $4.(ast.TimeUnitType), } } | "SYSTEM_TIME" "LIMIT" LengthNum { $$ = &ast.PartitionMethod{ Tp: model.PartitionTypeSystemTime, Limit: $3.(uint64), } } | "SYSTEM_TIME" { $$ = &ast.PartitionMethod{ Tp: model.PartitionTypeSystemTime, } } LinearOpt: { $$ = "" } | "LINEAR" { $$ = $1 } SubPartitionOpt: { $$ = nil } | "SUBPARTITION" "BY" SubPartitionMethod SubPartitionNumOpt { method := $3.(*ast.PartitionMethod) method.Num = $4.(uint64) $$ = method } SubPartitionNumOpt: { $$ = uint64(0) } | "SUBPARTITIONS" LengthNum { res := $2.(uint64) if res == 0 { yylex.AppendError(ast.ErrNoParts.GenWithStackByArgs("subpartitions")) return 1 } $$ = res } PartitionNumOpt: { $$ = uint64(0) } | "PARTITIONS" LengthNum { res := $2.(uint64) if res == 0 { yylex.AppendError(ast.ErrNoParts.GenWithStackByArgs("partitions")) return 1 } $$ = res } PartitionDefinitionListOpt: /* empty */ %prec lowerThanCreateTableSelect { $$ = nil } | '(' PartitionDefinitionList ')' { $$ = $2.([]*ast.PartitionDefinition) } PartitionDefinitionList: PartitionDefinition { $$ = []*ast.PartitionDefinition{$1.(*ast.PartitionDefinition)} } | PartitionDefinitionList ',' PartitionDefinition { $$ = append($1.([]*ast.PartitionDefinition), $3.(*ast.PartitionDefinition)) } PartitionDefinition: "PARTITION" Identifier PartDefValuesOpt PartDefOptionList SubPartDefinitionListOpt { $$ = &ast.PartitionDefinition{ Name: model.NewCIStr($2), Clause: $3.(ast.PartitionDefinitionClause), Options: $4.([]*ast.TableOption), Sub: $5.([]*ast.SubPartitionDefinition), } } SubPartDefinitionListOpt: /*empty*/ { $$ = make([]*ast.SubPartitionDefinition, 0) } | '(' SubPartDefinitionList ')' { $$ = $2 } SubPartDefinitionList: SubPartDefinition { $$ = []*ast.SubPartitionDefinition{$1.(*ast.SubPartitionDefinition)} } | SubPartDefinitionList ',' SubPartDefinition { list := $1.([]*ast.SubPartitionDefinition) $$ = append(list, $3.(*ast.SubPartitionDefinition)) } SubPartDefinition: "SUBPARTITION" Identifier PartDefOptionList { $$ = &ast.SubPartitionDefinition{ Name: model.NewCIStr($2), Options: $3.([]*ast.TableOption), } } PartDefOptionList: /*empty*/ { $$ = make([]*ast.TableOption, 0) } | PartDefOptionList PartDefOption { list := $1.([]*ast.TableOption) $$ = append(list, $2.(*ast.TableOption)) } PartDefOption: "COMMENT" EqOpt stringLit { $$ = &ast.TableOption{Tp: ast.TableOptionComment, StrValue: $3} } | "ENGINE" EqOpt StringName { $$ = &ast.TableOption{Tp: ast.TableOptionEngine, StrValue: $3.(string)} } | "STORAGE" "ENGINE" EqOpt StringName { $$ = &ast.TableOption{Tp: ast.TableOptionEngine, StrValue: $4.(string)} } | "INSERT_METHOD" EqOpt StringName { $$ = &ast.TableOption{Tp: ast.TableOptionInsertMethod, StrValue: $3.(string)} } | "DATA" "DIRECTORY" EqOpt stringLit { $$ = &ast.TableOption{Tp: ast.TableOptionDataDirectory, StrValue: $4} } | "INDEX" "DIRECTORY" EqOpt stringLit { $$ = &ast.TableOption{Tp: ast.TableOptionIndexDirectory, StrValue: $4} } | "MAX_ROWS" EqOpt LengthNum { $$ = &ast.TableOption{Tp: ast.TableOptionMaxRows, UintValue: $3.(uint64)} } | "MIN_ROWS" EqOpt LengthNum { $$ = &ast.TableOption{Tp: ast.TableOptionMinRows, UintValue: $3.(uint64)} } | "TABLESPACE" EqOpt Identifier { $$ = &ast.TableOption{Tp: ast.TableOptionTablespace, StrValue: $3} } | "NODEGROUP" EqOpt LengthNum { $$ = &ast.TableOption{Tp: ast.TableOptionNodegroup, UintValue: $3.(uint64)} } PartDefValuesOpt: { $$ = &ast.PartitionDefinitionClauseNone{} } | "VALUES" "LESS" "THAN" "MAXVALUE" { $$ = &ast.PartitionDefinitionClauseLessThan{ Exprs: []ast.ExprNode{&ast.MaxValueExpr{}}, } } | "VALUES" "LESS" "THAN" '(' MaxValueOrExpressionList ')' { $$ = &ast.PartitionDefinitionClauseLessThan{ Exprs: $5.([]ast.ExprNode), } } | "DEFAULT" { $$ = &ast.PartitionDefinitionClauseIn{} } | "VALUES" "IN" '(' MaxValueOrExpressionList ')' { exprs := $4.([]ast.ExprNode) values := make([][]ast.ExprNode, 0, len(exprs)) for _, expr := range exprs { if row, ok := expr.(*ast.RowExpr); ok { values = append(values, row.Values) } else { values = append(values, []ast.ExprNode{expr}) } } $$ = &ast.PartitionDefinitionClauseIn{Values: values} } | "HISTORY" { $$ = &ast.PartitionDefinitionClauseHistory{Current: false} } | "CURRENT" { $$ = &ast.PartitionDefinitionClauseHistory{Current: true} } DuplicateOpt: { $$ = ast.OnDuplicateKeyHandlingError } | "IGNORE" { $$ = ast.OnDuplicateKeyHandlingIgnore } | "REPLACE" { $$ = ast.OnDuplicateKeyHandlingReplace } AsOpt: {} | "AS" {} CreateTableSelectOpt: /* empty */ { $$ = &ast.CreateTableStmt{} } | SelectStmt { $$ = &ast.CreateTableStmt{Select: $1} } | UnionStmt { $$ = &ast.CreateTableStmt{Select: $1} } | SubSelect %prec createTableSelect // TODO: We may need better solution as issue #320. { $$ = &ast.CreateTableStmt{Select: $1} } CreateViewSelectOpt: SelectStmt { $$ = $1.(*ast.SelectStmt) } | UnionStmt { $$ = $1.(*ast.UnionStmt) } | '(' SelectStmt ')' { $$ = $2.(*ast.SelectStmt) } | '(' UnionStmt ')' { $$ = $2.(*ast.UnionStmt) } LikeTableWithOrWithoutParen: "LIKE" TableName { $$ = $2 } | '(' "LIKE" TableName ')' { $$ = $3 } /******************************************************************* * * Create View Statement * * Example: * CREATE VIEW OR REPLACE ALGORITHM = MERGE DEFINER="root@localhost" SQL SECURITY = definer view_name (col1,col2) * as select Col1,Col2 from table WITH LOCAL CHECK OPTION *******************************************************************/ CreateViewStmt: "CREATE" OrReplace ViewAlgorithm ViewDefiner ViewSQLSecurity "VIEW" ViewName ViewFieldList "AS" CreateViewSelectOpt ViewCheckOption { startOffset := parser.startOffset(&yyS[yypt-1]) selStmt := $10.(ast.StmtNode) selStmt.SetText(strings.TrimSpace(parser.src[startOffset:])) x := &ast.CreateViewStmt { OrReplace: $2.(bool), ViewName: $7.(*ast.TableName), Select: selStmt, Algorithm: $3.(model.ViewAlgorithm), Definer: $4.(*auth.UserIdentity), Security: $5.(model.ViewSecurity), } if $8 != nil{ x.Cols = $8.([]model.CIStr) } if $11 !=nil { x.CheckOption = $11.(model.ViewCheckOption) endOffset := parser.startOffset(&yyS[yypt]) selStmt.SetText(strings.TrimSpace(parser.src[startOffset:endOffset])) } else { x.CheckOption = model.CheckOptionCascaded } $$ = x } OrReplace: { $$ = false } | "OR" "REPLACE" { $$ = true } ViewAlgorithm: /* EMPTY */ { $$ = model.AlgorithmUndefined } | "ALGORITHM" "=" "UNDEFINED" { $$ = model.AlgorithmUndefined } | "ALGORITHM" "=" "MERGE" { $$ = model.AlgorithmMerge } | "ALGORITHM" "=" "TEMPTABLE" { $$ = model.AlgorithmTemptable } ViewDefiner: /* EMPTY */ { $$ = &auth.UserIdentity{CurrentUser: true} } | "DEFINER" "=" Username { $$ = $3 } ViewSQLSecurity: /* EMPTY */ { $$ = model.SecurityDefiner } | "SQL" "SECURITY" "DEFINER" { $$ = model.SecurityDefiner } | "SQL" "SECURITY" "INVOKER" { $$ = model.SecurityInvoker } ViewName: TableName { $$ = $1.(*ast.TableName) } ViewFieldList: /* Empty */ { $$ = nil } | '(' ColumnList ')' { $$ = $2.([]model.CIStr) } ColumnList: Identifier { $$ = []model.CIStr{model.NewCIStr($1)} } | ColumnList ',' Identifier { $$ = append($1.([]model.CIStr), model.NewCIStr($3)) } ViewCheckOption: /* EMPTY */ { $$ = nil } | "WITH" "CASCADED" "CHECK" "OPTION" { $$ = model.CheckOptionCascaded } | "WITH" "LOCAL" "CHECK" "OPTION" { $$ = model.CheckOptionLocal } /****************************************************************** * Do statement * See https://dev.mysql.com/doc/refman/5.7/en/do.html ******************************************************************/ DoStmt: "DO" ExpressionList { $$ = &ast.DoStmt { Exprs: $2.([]ast.ExprNode), } } /******************************************************************* * * Delete Statement * *******************************************************************/ DeleteFromStmt: "DELETE" TableOptimizerHints PriorityOpt QuickOptional IgnoreOptional "FROM" TableName TableAsNameOpt IndexHintListOpt WhereClauseOptional OrderByOptional LimitClause { // Single Table tn := $7.(*ast.TableName) tn.IndexHints = $9.([]*ast.IndexHint) join := &ast.Join{Left: &ast.TableSource{Source: tn, AsName: $8.(model.CIStr)}, Right: nil} x := &ast.DeleteStmt{ TableRefs: &ast.TableRefsClause{TableRefs: join}, Priority: $3.(mysql.PriorityEnum), Quick: $4.(bool), IgnoreErr: $5.(bool), } if $10 != nil { x.Where = $10.(ast.ExprNode) } if $11 != nil { x.Order = $11.(*ast.OrderByClause) } if $12 != nil { x.Limit = $12.(*ast.Limit) } $$ = x } | "DELETE" TableOptimizerHints PriorityOpt QuickOptional IgnoreOptional TableNameList "FROM" TableRefs WhereClauseOptional { // Multiple Table x := &ast.DeleteStmt{ Priority: $3.(mysql.PriorityEnum), Quick: $4.(bool), IgnoreErr: $5.(bool), IsMultiTable: true, BeforeFrom: true, Tables: &ast.DeleteTableList{Tables: $6.([]*ast.TableName)}, TableRefs: &ast.TableRefsClause{TableRefs: $8.(*ast.Join)}, } if $2 != nil { x.TableHints = $2.([]*ast.TableOptimizerHint) } if $9 != nil { x.Where = $9.(ast.ExprNode) } $$ = x } | "DELETE" TableOptimizerHints PriorityOpt QuickOptional IgnoreOptional "FROM" TableNameList "USING" TableRefs WhereClauseOptional { // Multiple Table x := &ast.DeleteStmt{ Priority: $3.(mysql.PriorityEnum), Quick: $4.(bool), IgnoreErr: $5.(bool), IsMultiTable: true, Tables: &ast.DeleteTableList{Tables: $7.([]*ast.TableName)}, TableRefs: &ast.TableRefsClause{TableRefs: $9.(*ast.Join)}, } if $2 != nil { x.TableHints = $2.([]*ast.TableOptimizerHint) } if $10 != nil { x.Where = $10.(ast.ExprNode) } $$ = x } DatabaseSym: "DATABASE" DropDatabaseStmt: "DROP" DatabaseSym IfExists DBName { $$ = &ast.DropDatabaseStmt{IfExists: $3.(bool), Name: $4.(string)} } /****************************************************************** * Drop Index Statement * See https://dev.mysql.com/doc/refman/8.0/en/drop-index.html * * DROP INDEX index_name ON tbl_name * [algorithm_option | lock_option] ... * * algorithm_option: * ALGORITHM [=] {DEFAULT|INPLACE|COPY} * * lock_option: * LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE} ******************************************************************/ DropIndexStmt: "DROP" "INDEX" IfExists Identifier "ON" TableName IndexLockAndAlgorithmOpt { var indexLockAndAlgorithm *ast.IndexLockAndAlgorithm if $7 != nil { indexLockAndAlgorithm = $7.(*ast.IndexLockAndAlgorithm) if indexLockAndAlgorithm.LockTp == ast.LockTypeDefault && indexLockAndAlgorithm.AlgorithmTp == ast.AlgorithmTypeDefault { indexLockAndAlgorithm = nil } } $$ = &ast.DropIndexStmt{IfExists: $3.(bool), IndexName: $4, Table: $6.(*ast.TableName), LockAlg: indexLockAndAlgorithm} } DropTableStmt: "DROP" OptTemporary TableOrTables IfExists TableNameList RestrictOrCascadeOpt { $$ = &ast.DropTableStmt{IfExists: $4.(bool), Tables: $5.([]*ast.TableName), IsView: false, IsTemporary: $2.(bool)} } OptTemporary: /* empty */ { $$ = false; } | "TEMPORARY" { $$ = true yylex.AppendError(yylex.Errorf("TiDB doesn't support TEMPORARY TABLE, TEMPORARY will be parsed but ignored.")) parser.lastErrorAsWarn() } ; DropViewStmt: "DROP" "VIEW" TableNameList RestrictOrCascadeOpt { $$ = &ast.DropTableStmt{Tables: $3.([]*ast.TableName), IsView: true} } | "DROP" "VIEW" "IF" "EXISTS" TableNameList RestrictOrCascadeOpt { $$ = &ast.DropTableStmt{IfExists: true, Tables: $5.([]*ast.TableName), IsView: true} } DropUserStmt: "DROP" "USER" UsernameList { $$ = &ast.DropUserStmt{IsDropRole: false, IfExists: false, UserList: $3.([]*auth.UserIdentity)} } | "DROP" "USER" "IF" "EXISTS" UsernameList { $$ = &ast.DropUserStmt{IsDropRole: false, IfExists: true, UserList: $5.([]*auth.UserIdentity)} } DropRoleStmt: "DROP" "ROLE" RolenameList { tmp := make([]*auth.UserIdentity, 0, 10) roleList := $3.([]*auth.RoleIdentity) for _, r := range roleList { tmp = append(tmp, &auth.UserIdentity{Username:r.Username, Hostname: r.Hostname}) } $$ = &ast.DropUserStmt{IsDropRole: true, IfExists: false, UserList: tmp} } | "DROP" "ROLE" "IF" "EXISTS" RolenameList { tmp := make([]*auth.UserIdentity, 0, 10) roleList := $5.([]*auth.RoleIdentity) for _, r := range roleList { tmp = append(tmp, &auth.UserIdentity{Username:r.Username, Hostname: r.Hostname}) } $$ = &ast.DropUserStmt{IsDropRole: true, IfExists: true, UserList: tmp} } DropStatsStmt: "DROP" "STATS" TableName { $$ = &ast.DropStatsStmt{Table: $3.(*ast.TableName)} } RestrictOrCascadeOpt: {} | "RESTRICT" | "CASCADE" TableOrTables: "TABLE" | "TABLES" EqOpt: {} | eq EmptyStmt: /* EMPTY */ { $$ = nil } TraceStmt: "TRACE" TraceableStmt { $$ = &ast.TraceStmt{ Stmt: $2, Format: "json", } startOffset := parser.startOffset(&yyS[yypt]) $2.SetText(string(parser.src[startOffset:])) } | "TRACE" "FORMAT" "=" stringLit TraceableStmt { $$ = &ast.TraceStmt{ Stmt: $5, Format: $4, } startOffset := parser.startOffset(&yyS[yypt]) $5.SetText(string(parser.src[startOffset:])) } ExplainSym: "EXPLAIN" | "DESCRIBE" | "DESC" ExplainStmt: ExplainSym TableName { $$ = &ast.ExplainStmt{ Stmt: &ast.ShowStmt{ Tp: ast.ShowColumns, Table: $2.(*ast.TableName), }, } } | ExplainSym TableName ColumnName { $$ = &ast.ExplainStmt{ Stmt: &ast.ShowStmt{ Tp: ast.ShowColumns, Table: $2.(*ast.TableName), Column: $3.(*ast.ColumnName), }, } } | ExplainSym ExplainableStmt { $$ = &ast.ExplainStmt{ Stmt: $2, Format: "row", } } | ExplainSym "FOR" "CONNECTION" NUM { $$ = &ast.ExplainForStmt{ Format: "row", ConnectionID: getUint64FromNUM($4), } } | ExplainSym "FORMAT" "=" stringLit "FOR" "CONNECTION" NUM { $$ = &ast.ExplainForStmt{ Format: $4, ConnectionID: getUint64FromNUM($7), } } | ExplainSym "FORMAT" "=" stringLit ExplainableStmt { $$ = &ast.ExplainStmt{ Stmt: $5, Format: $4, } } | ExplainSym "FORMAT" "=" ExplainFormatType "FOR" "CONNECTION" NUM { $$ = &ast.ExplainForStmt{ Format: $4.(string), ConnectionID: getUint64FromNUM($7), } } | ExplainSym "FORMAT" "=" ExplainFormatType ExplainableStmt { $$ = &ast.ExplainStmt{ Stmt: $5, Format: $4.(string), } } | ExplainSym "ANALYZE" ExplainableStmt { $$ = &ast.ExplainStmt { Stmt: $3, Format: "row", Analyze: true, } } ExplainFormatType: "TRADITIONAL" { $$ = "row" } | "JSON" { $$ = "json" } LengthNum: NUM { $$ = getUint64FromNUM($1) } NUM: intLit Expression: singleAtIdentifier assignmentEq Expression %prec assignmentEq { v := $1 v = strings.TrimPrefix(v, "@") $$ = &ast.VariableExpr{ Name: v, IsGlobal: false, IsSystem: false, Value: $3, } } | Expression logOr Expression %prec pipes { $$ = &ast.BinaryOperationExpr{Op: opcode.LogicOr, L: $1, R: $3} } | Expression "XOR" Expression %prec xor { $$ = &ast.BinaryOperationExpr{Op: opcode.LogicXor, L: $1, R: $3} } | Expression logAnd Expression %prec andand { $$ = &ast.BinaryOperationExpr{Op: opcode.LogicAnd, L: $1, R: $3} } | "NOT" Expression %prec not { expr, ok := $2.(*ast.ExistsSubqueryExpr) if ok { expr.Not = true $$ = $2 } else { $$ = &ast.UnaryOperationExpr{Op: opcode.Not, V: $2} } } | BoolPri IsOrNotOp trueKwd %prec is { $$ = &ast.IsTruthExpr{Expr:$1, Not: !$2.(bool), True: int64(1)} } | BoolPri IsOrNotOp falseKwd %prec is { $$ = &ast.IsTruthExpr{Expr:$1, Not: !$2.(bool), True: int64(0)} } | BoolPri IsOrNotOp "UNKNOWN" %prec is { /* https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_is */ $$ = &ast.IsNullExpr{Expr: $1, Not: !$2.(bool)} } | BoolPri MaxValueOrExpression: "MAXVALUE" { $$ = &ast.MaxValueExpr{} } | Expression { $$ = $1 } logOr: pipesAsOr | "OR" logAnd: "&&" | "AND" ExpressionList: Expression { $$ = []ast.ExprNode{$1} } | ExpressionList ',' Expression { $$ = append($1.([]ast.ExprNode), $3) } MaxValueOrExpressionList: MaxValueOrExpression { $$ = []ast.ExprNode{$1} } | MaxValueOrExpressionList ',' MaxValueOrExpression { $$ = append($1.([]ast.ExprNode), $3) } ExpressionListOpt: { $$ = []ast.ExprNode{} } | ExpressionList FuncDatetimePrecListOpt: { $$ = []ast.ExprNode{} } | FuncDatetimePrecList { $$ = $1 } FuncDatetimePrecList: intLit { expr := ast.NewValueExpr($1) $$ = []ast.ExprNode{expr} } BoolPri: BoolPri IsOrNotOp "NULL" %prec is { $$ = &ast.IsNullExpr{Expr: $1, Not: !$2.(bool)} } | BoolPri CompareOp PredicateExpr %prec eq { $$ = &ast.BinaryOperationExpr{Op: $2.(opcode.Op), L: $1, R: $3} } | BoolPri CompareOp AnyOrAll SubSelect %prec eq { sq := $4.(*ast.SubqueryExpr) sq.MultiRows = true $$ = &ast.CompareSubqueryExpr{Op: $2.(opcode.Op), L: $1, R: sq, All: $3.(bool)} } | BoolPri CompareOp singleAtIdentifier assignmentEq PredicateExpr %prec assignmentEq { v := $3 v = strings.TrimPrefix(v, "@") variable := &ast.VariableExpr{ Name: v, IsGlobal: false, IsSystem: false, Value: $5, } $$ = &ast.BinaryOperationExpr{Op: $2.(opcode.Op), L: $1, R: variable} } | PredicateExpr CompareOp: ">=" { $$ = opcode.GE } | '>' { $$ = opcode.GT } | "<=" { $$ = opcode.LE } | '<' { $$ = opcode.LT } | "!=" { $$ = opcode.NE } | "<>" { $$ = opcode.NE } | "=" { $$ = opcode.EQ } | "<=>" { $$ = opcode.NullEQ } BetweenOrNotOp: "BETWEEN" { $$ = true } | "NOT" "BETWEEN" { $$ = false } IsOrNotOp: "IS" { $$ = true } | "IS" "NOT" { $$ = false } InOrNotOp: "IN" { $$ = true } | "NOT" "IN" { $$ = false } LikeOrNotOp: "LIKE" { $$ = true } | "NOT" "LIKE" { $$ = false } RegexpOrNotOp: RegexpSym { $$ = true } | "NOT" RegexpSym { $$ = false } AnyOrAll: "ANY" { $$ = false } | "SOME" { $$ = false } | "ALL" { $$ = true } PredicateExpr: BitExpr InOrNotOp '(' ExpressionList ')' { $$ = &ast.PatternInExpr{Expr: $1, Not: !$2.(bool), List: $4.([]ast.ExprNode)} } | BitExpr InOrNotOp SubSelect { sq := $3.(*ast.SubqueryExpr) sq.MultiRows = true $$ = &ast.PatternInExpr{Expr: $1, Not: !$2.(bool), Sel: sq} } | BitExpr BetweenOrNotOp BitExpr "AND" PredicateExpr { $$ = &ast.BetweenExpr{ Expr: $1, Left: $3, Right: $5, Not: !$2.(bool), } } | BitExpr LikeOrNotOp SimpleExpr LikeEscapeOpt { escape := $4.(string) if len(escape) > 1 { yylex.AppendError(ErrWrongArguments.GenWithStackByArgs("ESCAPE")) return 1 } else if len(escape) == 0 { escape = "\\" } $$ = &ast.PatternLikeExpr{ Expr: $1, Pattern: $3, Not: !$2.(bool), Escape: escape[0], } } | BitExpr RegexpOrNotOp SimpleExpr { $$ = &ast.PatternRegexpExpr{Expr: $1, Pattern: $3, Not: !$2.(bool)} } | BitExpr RegexpSym: "REGEXP" | "RLIKE" LikeEscapeOpt: %prec empty { $$ = "\\" } | "ESCAPE" stringLit { $$ = $2 } Field: '*' { $$ = &ast.SelectField{WildCard: &ast.WildCardField{}} } | Identifier '.' '*' { wildCard := &ast.WildCardField{Table: model.NewCIStr($1)} $$ = &ast.SelectField{WildCard: wildCard} } | Identifier '.' Identifier '.' '*' { wildCard := &ast.WildCardField{Schema: model.NewCIStr($1), Table: model.NewCIStr($3)} $$ = &ast.SelectField{WildCard: wildCard} } | Expression FieldAsNameOpt { expr := $1 asName := $2.(string) $$ = &ast.SelectField{Expr: expr, AsName: model.NewCIStr(asName)} } | '{' Identifier Expression '}' FieldAsNameOpt { /* * ODBC escape syntax. * See https://dev.mysql.com/doc/refman/5.7/en/expressions.html */ expr := $3 asName := $5.(string) $$ = &ast.SelectField{Expr: expr, AsName: model.NewCIStr(asName)} } FieldAsNameOpt: /* EMPTY */ { $$ = "" } | FieldAsName { $$ = $1 } FieldAsName: Identifier { $$ = $1 } | "AS" Identifier { $$ = $2 } | stringLit { $$ = $1 } | "AS" stringLit { $$ = $2 } FieldList: Field { field := $1.(*ast.SelectField) field.Offset = parser.startOffset(&yyS[yypt]) $$ = []*ast.SelectField{field} } | FieldList ',' Field { fl := $1.([]*ast.SelectField) last := fl[len(fl)-1] if last.Expr != nil && last.AsName.O == "" { lastEnd := parser.endOffset(&yyS[yypt-1]) last.SetText(parser.src[last.Offset:lastEnd]) } newField := $3.(*ast.SelectField) newField.Offset = parser.startOffset(&yyS[yypt]) $$ = append(fl, newField) } GroupByClause: "GROUP" "BY" ByList { $$ = &ast.GroupByClause{Items: $3.([]*ast.ByItem)} } HavingClause: { $$ = nil } | "HAVING" Expression { $$ = &ast.HavingClause{Expr: $2} } IfExists: { $$ = false } | "IF" "EXISTS" { $$ = true } IfNotExists: { $$ = false } | "IF" "NOT" "EXISTS" { $$ = true } IgnoreOptional: { $$ = false } | "IGNORE" { $$ = true } IndexName: { $$ = "" } | Identifier { //"index name" $$ = $1 } IndexOptionList: { $$ = nil } | IndexOptionList IndexOption { // Merge the options if $1 == nil { $$ = $2 } else { opt1 := $1.(*ast.IndexOption) opt2 := $2.(*ast.IndexOption) if len(opt2.Comment) > 0 { opt1.Comment = opt2.Comment } else if opt2.Tp != 0 { opt1.Tp = opt2.Tp } else if opt2.KeyBlockSize > 0 { opt1.KeyBlockSize = opt2.KeyBlockSize } else if len(opt2.ParserName.O) > 0 { opt1.ParserName = opt2.ParserName } else if opt2.Visibility != ast.IndexVisibilityDefault { opt1.Visibility = opt2.Visibility } $$ = opt1 } } IndexOption: "KEY_BLOCK_SIZE" EqOpt LengthNum { $$ = &ast.IndexOption{ KeyBlockSize: $3.(uint64), } } | IndexType { $$ = &ast.IndexOption { Tp: $1.(model.IndexType), } } | "WITH" "PARSER" Identifier { $$ = &ast.IndexOption { ParserName: model.NewCIStr($3), } yylex.AppendError(yylex.Errorf("The WITH PARASER clause is parsed but ignored by all storage engines.")) parser.lastErrorAsWarn() } | "COMMENT" stringLit { $$ = &ast.IndexOption { Comment: $2, } } | IndexInvisible { $$ = &ast.IndexOption { Visibility: $1.(ast.IndexVisibility), } } /* See: https://github.com/mysql/mysql-server/blob/8.0/sql/sql_yacc.yy#L7179 The syntax for defining an index is: ... INDEX [index_name] [USING|TYPE] ... The problem is that whereas USING is a reserved word, TYPE is not. We can still handle it if an index name is supplied, i.e.: ... INDEX type TYPE ... here the index's name is unmbiguously 'type', but for this: ... INDEX TYPE ... it's impossible to know what this actually mean - is 'type' the name or the type? For this reason we accept the TYPE syntax only if a name is supplied. */ IndexNameAndTypeOpt: IndexName { $$ = []interface{}{$1, nil} } | IndexName "USING" IndexTypeName { $$ = []interface{}{$1, $3} } | Identifier "TYPE" IndexTypeName { $$ = []interface{}{$1, $3} } IndexTypeOpt: { $$ = nil } | IndexType { $$ = $1 } IndexType: "USING" IndexTypeName { $$ = $2 } | "TYPE" IndexTypeName { $$ = $2 } IndexTypeName: "BTREE" { $$ = model.IndexTypeBtree } | "HASH" { $$ = model.IndexTypeHash } | "RTREE" { $$ = model.IndexTypeRtree } IndexInvisible: "VISIBLE" { $$ = ast.IndexVisibilityVisible } | "INVISIBLE" { $$ = ast.IndexVisibilityInvisible } /**********************************Identifier********************************************/ Identifier: identifier | UnReservedKeyword | NotKeywordToken | TiDBKeyword UnReservedKeyword: "ACTION" | "ASCII" | "AUTO_INCREMENT" | "AFTER" | "ALWAYS" | "AVG" | "BEGIN" | "BIT" | "BOOL" | "BOOLEAN" | "BTREE" | "BYTE" | "CLEANUP" | "CHARSET" | "COLUMNS" | "COMMIT" | "COMPACT" | "COMPRESSED" | "CONSISTENT" | "CURRENT" | "DATA" | "DATE" %prec lowerThanStringLitToken| "DATETIME" | "DAY" | "DEALLOCATE" | "DO" | "DUPLICATE" | "DYNAMIC" | "ENCRYPTION" | "END" | "ENFORCED" | "ENGINE" | "ENGINES" | "ENUM" | "ERRORS" | "ESCAPE" | "EXECUTE" | "FIELDS" | "FIRST" | "FIXED" | "FLUSH" | "FOLLOWING" | "FORMAT" | "FULL" |"GLOBAL" | "HASH" | "HOUR" | "INSERT_METHOD" | "LESS" | "LOCAL" | "LAST" | "NAMES" | "OFFSET" | "PASSWORD" %prec lowerThanEq | "PREPARE" | "QUICK" | "REBUILD" | "REDUNDANT" | "REORGANIZE" | "ROLE" |"ROLLBACK" | "SESSION" | "SIGNED" | "SHUTDOWN" | "SNAPSHOT" | "START" | "STATUS" | "OPEN"| "SUBPARTITIONS" | "SUBPARTITION" | "TABLES" | "TABLESPACE" | "TEXT" | "THAN" | "TIME" %prec lowerThanStringLitToken | "TIMESTAMP" %prec lowerThanStringLitToken | "TRACE" | "TRANSACTION" | "TRUNCATE" | "UNBOUNDED" | "UNKNOWN" | "VALUE" | "WARNINGS" | "YEAR" | "MODE" | "WEEK" | "ANY" | "SOME" | "USER" | "IDENTIFIED" | "COLLATION" | "COMMENT" | "AVG_ROW_LENGTH" | "CONNECTION" | "CHECKSUM" | "COMPRESSION" | "KEY_BLOCK_SIZE" | "MASTER" | "MAX_ROWS" | "MIN_ROWS" | "NATIONAL" | "NCHAR" | "ROW_FORMAT" | "QUARTER" | "GRANTS" | "TRIGGERS" | "DELAY_KEY_WRITE" | "ISOLATION" | "JSON" | "REPEATABLE" | "RESPECT" | "COMMITTED" | "UNCOMMITTED" | "ONLY" | "SERIAL" | "SERIALIZABLE" | "LEVEL" | "VARIABLES" | "SQL_CACHE" | "INDEXES" | "PROCESSLIST" | "SQL_NO_CACHE" | "DISABLE" | "ENABLE" | "REVERSE" | "PRIVILEGES" | "NO" | "BINLOG" | "FUNCTION" | "VIEW" | "BINDING" | "BINDINGS" | "MODIFY" | "EVENTS" | "PARTITIONS" | "NONE" | "NULLS" | "SUPER" | "EXCLUSIVE" | "STATS_PERSISTENT" | "STATS_AUTO_RECALC" | "ROW_COUNT" | "COALESCE" | "MONTH" | "PROCESS" | "PROFILE" | "PROFILES" | "MICROSECOND" | "MINUTE" | "PLUGINS" | "PRECEDING" | "QUERY" | "QUERIES" | "SECOND" | "SEPARATOR" | "SHARE" | "SHARED" | "SLOW" | "MAX_CONNECTIONS_PER_HOUR" | "MAX_QUERIES_PER_HOUR" | "MAX_UPDATES_PER_HOUR" | "MAX_USER_CONNECTIONS" | "REPLICATION" | "CLIENT" | "SLAVE" | "RELOAD" | "TEMPORARY" | "ROUTINE" | "EVENT" | "ALGORITHM" | "DEFINER" | "INVOKER" | "MERGE" | "TEMPTABLE" | "UNDEFINED" | "SECURITY" | "CASCADED" | "RECOVER" | "CIPHER" | "SUBJECT" | "ISSUER" | "X509" | "NEVER" | "EXPIRE" | "ACCOUNT" | "INCREMENTAL" | "CPU" | "MEMORY" | "BLOCK" | "IO" | "CONTEXT" | "SWITCHES" | "PAGE" | "FAULTS" | "IPC" | "SWAPS" | "SOURCE" | "TRADITIONAL" | "SQL_BUFFER_RESULT" | "DIRECTORY" | "HISTORY" | "LIST" | "NODEGROUP" | "SYSTEM_TIME" | "PARTIAL" | "SIMPLE" | "REMOVE" | "PARTITIONING" | "STORAGE" | "DISK" | "STATS_SAMPLE_PAGES" | "SECONDARY_ENGINE" | "SECONDARY_LOAD" | "SECONDARY_UNLOAD" | "VALIDATION" | "WITHOUT" | "RTREE" | "EXCHANGE" | "COLUMN_FORMAT" | "REPAIR" | "IMPORT" | "DISCARD" | "TABLE_CHECKSUM" | "UNICODE" | "SQL_TSI_DAY" | "SQL_TSI_HOUR" | "SQL_TSI_MINUTE" | "SQL_TSI_MONTH" | "SQL_TSI_QUARTER" | "SQL_TSI_SECOND" | "SQL_TSI_WEEK" | "SQL_TSI_YEAR" | "INVISIBLE" | "VISIBLE" | "TYPE" | "NOWAIT" | "REPLICA" | "LOCATION" | "LABELS" TiDBKeyword: "ADMIN" | "AGG_TO_COP" |"BUCKETS" | "BUILTINS" | "CANCEL" | "CMSKETCH" | "DDL" | "DEPTH" | "DRAINER" | "JOBS" | "JOB" | "NODE_ID" | "NODE_STATE" | "PUMP" | "SAMPLES" | "STATS" | "STATS_META" | "STATS_HISTOGRAMS" | "STATS_BUCKETS" | "STATS_HEALTHY" | "TIDB" | "HASH_JOIN" | "SM_JOIN" | "INL_JOIN" | "HASH_AGG" | "STREAM_AGG" | "USE_INDEX" | "IGNORE_INDEX" | "USE_INDEX_MERGE" | "NO_INDEX_MERGE" | "USE_TOJA" | "ENABLE_PLAN_CACHE" | "USE_PLAN_CACHE" | "READ_CONSISTENT_REPLICA" | "READ_FROM_STORAGE" | "QB_NAME" | "QUERY_TYPE" | "MEMORY_QUOTA" | "OLAP" | "OLTP" | "TOPN" | "TIKV" | "TIFLASH" | "SPLIT" | "OPTIMISTIC" | "PESSIMISTIC" | "WIDTH" | "REGIONS" | "REGION" NotKeywordToken: "ADDDATE" | "BIT_AND" | "BIT_OR" | "BIT_XOR" | "CAST" | "COPY" | "COUNT" | "CURTIME" | "DATE_ADD" | "DATE_SUB" | "EXTRACT" | "GET_FORMAT" | "GROUP_CONCAT" | "INPLACE" | "INSTANT" | "INTERNAL" |"MIN" | "MAX" | "MAX_EXECUTION_TIME" | "NOW" | "RECENT" | "POSITION" | "SUBDATE" | "SUBSTRING" | "SUM" | "STD" | "STDDEV" | "STDDEV_POP" | "STDDEV_SAMP" | "VARIANCE" | "VAR_POP" | "VAR_SAMP" | "TIMESTAMPADD" | "TIMESTAMPDIFF" | "TOKUDB_DEFAULT" | "TOKUDB_FAST" | "TOKUDB_LZMA" | "TOKUDB_QUICKLZ" | "TOKUDB_SNAPPY" | "TOKUDB_SMALL" | "TOKUDB_UNCOMPRESSED" | "TOKUDB_ZLIB" | "TOP" | "TRIM" | "NEXT_ROW_ID" | "EXPR_PUSHDOWN_BLACKLIST" | "OPT_RULE_BLACKLIST" /************************************************************************************ * * Insert Statements * * TODO: support PARTITION **********************************************************************************/ InsertIntoStmt: "INSERT" PriorityOpt IgnoreOptional IntoOpt TableName InsertValues OnDuplicateKeyUpdate { x := $6.(*ast.InsertStmt) x.Priority = $2.(mysql.PriorityEnum) x.IgnoreErr = $3.(bool) // Wraps many layers here so that it can be processed the same way as select statement. ts := &ast.TableSource{Source: $5.(*ast.TableName)} x.Table = &ast.TableRefsClause{TableRefs: &ast.Join{Left: ts}} if $7 != nil { x.OnDuplicate = $7.([]*ast.Assignment) } $$ = x } IntoOpt: {} | "INTO" InsertValues: '(' ColumnNameListOpt ')' ValueSym ValuesList { $$ = &ast.InsertStmt{ Columns: $2.([]*ast.ColumnName), Lists: $5.([][]ast.ExprNode), } } | '(' ColumnNameListOpt ')' SelectStmt { $$ = &ast.InsertStmt{Columns: $2.([]*ast.ColumnName), Select: $4.(*ast.SelectStmt)} } | '(' ColumnNameListOpt ')' '(' SelectStmt ')' { $$ = &ast.InsertStmt{Columns: $2.([]*ast.ColumnName), Select: $5.(*ast.SelectStmt)} } | '(' ColumnNameListOpt ')' UnionStmt { $$ = &ast.InsertStmt{Columns: $2.([]*ast.ColumnName), Select: $4.(*ast.UnionStmt)} } | ValueSym ValuesList %prec insertValues { $$ = &ast.InsertStmt{Lists: $2.([][]ast.ExprNode)} } | '(' SelectStmt ')' { $$ = &ast.InsertStmt{Select: $2.(*ast.SelectStmt)} } | SelectStmt { $$ = &ast.InsertStmt{Select: $1.(*ast.SelectStmt)} } | UnionStmt { $$ = &ast.InsertStmt{Select: $1.(*ast.UnionStmt)} } | "SET" ColumnSetValueList { $$ = &ast.InsertStmt{Setlist: $2.([]*ast.Assignment)} } ValueSym: "VALUE" | "VALUES" ValuesList: RowValue { $$ = [][]ast.ExprNode{$1.([]ast.ExprNode)} } | ValuesList ',' RowValue { $$ = append($1.([][]ast.ExprNode), $3.([]ast.ExprNode)) } RowValue: '(' ValuesOpt ')' { $$ = $2 } ValuesOpt: { $$ = []ast.ExprNode{} } | Values Values: Values ',' ExprOrDefault { $$ = append($1.([]ast.ExprNode), $3) } | ExprOrDefault { $$ = []ast.ExprNode{$1} } ExprOrDefault: Expression | "DEFAULT" { $$ = &ast.DefaultExpr{} } ColumnSetValue: ColumnName eq ExprOrDefault { $$ = &ast.Assignment{ Column: $1.(*ast.ColumnName), Expr: $3, } } ColumnSetValueList: { $$ = []*ast.Assignment{} } | ColumnSetValue { $$ = []*ast.Assignment{$1.(*ast.Assignment)} } | ColumnSetValueList ',' ColumnSetValue { $$ = append($1.([]*ast.Assignment), $3.(*ast.Assignment)) } /* * ON DUPLICATE KEY UPDATE col_name=expr [, col_name=expr] ... * See https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html */ OnDuplicateKeyUpdate: { $$ = nil } | "ON" "DUPLICATE" "KEY" "UPDATE" AssignmentList { $$ = $5 } /***********************************Insert Statements END************************************/ /************************************************************************************ * Replace Statements * See https://dev.mysql.com/doc/refman/5.7/en/replace.html * * TODO: support PARTITION **********************************************************************************/ ReplaceIntoStmt: "REPLACE" PriorityOpt IntoOpt TableName InsertValues { x := $5.(*ast.InsertStmt) x.IsReplace = true x.Priority = $2.(mysql.PriorityEnum) ts := &ast.TableSource{Source: $4.(*ast.TableName)} x.Table = &ast.TableRefsClause{TableRefs: &ast.Join{Left: ts}} $$ = x } /***********************************Replace Statements END************************************/ ODBCDateTimeType: "d" { $$ = ast.DateLiteral } | "t" { $$ = ast.TimeLiteral } | "ts" { $$ = ast.TimestampLiteral } Literal: "FALSE" { $$ = ast.NewValueExpr(false) } | "NULL" { $$ = ast.NewValueExpr(nil) } | "TRUE" { $$ = ast.NewValueExpr(true) } | floatLit { $$ = ast.NewValueExpr($1) } | decLit { $$ = ast.NewValueExpr($1) } | intLit { $$ = ast.NewValueExpr($1) } | StringLiteral %prec lowerThanStringLitToken { $$ = $1 } | "UNDERSCORE_CHARSET" stringLit { // See https://dev.mysql.com/doc/refman/5.7/en/charset-literal.html co, err := charset.GetDefaultCollation($1) if err != nil { yylex.AppendError(yylex.Errorf("Get collation error for charset: %s", $1)) return 1 } expr := ast.NewValueExpr($2) tp := expr.GetType() tp.Charset = $1 tp.Collate = co if tp.Collate == charset.CollationBin { tp.Flag |= mysql.BinaryFlag } $$ = expr } | hexLit { $$ = ast.NewValueExpr($1) } | bitLit { $$ = ast.NewValueExpr($1) } StringLiteral: stringLit { expr := ast.NewValueExpr($1) $$ = expr } | StringLiteral stringLit { valExpr := $1.(ast.ValueExpr) strLit := valExpr.GetString() expr := ast.NewValueExpr(strLit+$2) // Fix #4239, use first string literal as projection name. if valExpr.GetProjectionOffset() >= 0 { expr.SetProjectionOffset(valExpr.GetProjectionOffset()) } else { expr.SetProjectionOffset(len(strLit)) } $$ = expr } AlterOrderList: AlterOrderItem { $$ = []*ast.AlterOrderItem{$1.(*ast.AlterOrderItem)} } | AlterOrderList ',' AlterOrderItem { $$ = append($1.([]*ast.AlterOrderItem), $3.(*ast.AlterOrderItem)) } AlterOrderItem: ColumnName Order { $$ = &ast.AlterOrderItem{Column: $1.(*ast.ColumnName), Desc: $2.(bool)} } OrderBy: "ORDER" "BY" ByList { $$ = &ast.OrderByClause{Items: $3.([]*ast.ByItem)} } ByList: ByItem { $$ = []*ast.ByItem{$1.(*ast.ByItem)} } | ByList ',' ByItem { $$ = append($1.([]*ast.ByItem), $3.(*ast.ByItem)) } ByItem: Expression Order { expr := $1 valueExpr, ok := expr.(ast.ValueExpr) if ok { position, isPosition := valueExpr.GetValue().(int64) if isPosition { expr = &ast.PositionExpr{N: int(position)} } } $$ = &ast.ByItem{Expr: expr, Desc: $2.(bool)} } Order: /* EMPTY */ { $$ = false // ASC by default } | "ASC" { $$ = false } | "DESC" { $$ = true } OrderByOptional: { $$ = nil } | OrderBy { $$ = $1 } BitExpr: BitExpr '|' BitExpr %prec '|' { $$ = &ast.BinaryOperationExpr{Op: opcode.Or, L: $1, R: $3} } | BitExpr '&' BitExpr %prec '&' { $$ = &ast.BinaryOperationExpr{Op: opcode.And, L: $1, R: $3} } | BitExpr "<<" BitExpr %prec lsh { $$ = &ast.BinaryOperationExpr{Op: opcode.LeftShift, L: $1, R: $3} } | BitExpr ">>" BitExpr %prec rsh { $$ = &ast.BinaryOperationExpr{Op: opcode.RightShift, L: $1, R: $3} } | BitExpr '+' BitExpr %prec '+' { $$ = &ast.BinaryOperationExpr{Op: opcode.Plus, L: $1, R: $3} } | BitExpr '-' BitExpr %prec '-' { $$ = &ast.BinaryOperationExpr{Op: opcode.Minus, L: $1, R: $3} } | BitExpr '+' "INTERVAL" Expression TimeUnit %prec '+' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr("DATE_ADD"), Args: []ast.ExprNode{ $1, $4, &ast.TimeUnitExpr{Unit: $5.(ast.TimeUnitType)}, }, } } | BitExpr '-' "INTERVAL" Expression TimeUnit %prec '+' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr("DATE_SUB"), Args: []ast.ExprNode{ $1, $4, &ast.TimeUnitExpr{Unit: $5.(ast.TimeUnitType)}, }, } } | BitExpr '*' BitExpr %prec '*' { $$ = &ast.BinaryOperationExpr{Op: opcode.Mul, L: $1, R: $3} } | BitExpr '/' BitExpr %prec '/' { $$ = &ast.BinaryOperationExpr{Op: opcode.Div, L: $1, R: $3} } | BitExpr '%' BitExpr %prec '%' { $$ = &ast.BinaryOperationExpr{Op: opcode.Mod, L: $1, R: $3} } | BitExpr "DIV" BitExpr %prec div { $$ = &ast.BinaryOperationExpr{Op: opcode.IntDiv, L: $1, R: $3} } | BitExpr "MOD" BitExpr %prec mod { $$ = &ast.BinaryOperationExpr{Op: opcode.Mod, L: $1, R: $3} } | BitExpr '^' BitExpr { $$ = &ast.BinaryOperationExpr{Op: opcode.Xor, L: $1, R: $3} } | SimpleExpr SimpleIdent: Identifier { $$ = &ast.ColumnNameExpr{Name: &ast.ColumnName{ Name: model.NewCIStr($1), }} } | Identifier '.' Identifier { $$ = &ast.ColumnNameExpr{Name: &ast.ColumnName{ Table: model.NewCIStr($1), Name: model.NewCIStr($3), }} } | '.' Identifier '.' Identifier { $$ = &ast.ColumnNameExpr{Name: &ast.ColumnName{ Table: model.NewCIStr($2), Name: model.NewCIStr($4), }} } | Identifier '.' Identifier '.' Identifier { $$ = &ast.ColumnNameExpr{Name: &ast.ColumnName{ Schema: model.NewCIStr($1), Table: model.NewCIStr($3), Name: model.NewCIStr($5), }} } SimpleExpr: SimpleIdent | FunctionCallKeyword | FunctionCallNonKeyword | FunctionCallGeneric | SimpleExpr "COLLATE" StringName %prec neg { // TODO: Create a builtin function hold expr and collation. When do evaluation, convert expr result using the collation. $$ = $1 } | WindowFuncCall { $$ = $1.(*ast.WindowFuncExpr) } | Literal | paramMarker { $$ = ast.NewParamMarkerExpr(yyS[yypt].offset) } | Variable | SumExpr | '!' SimpleExpr %prec neg { $$ = &ast.UnaryOperationExpr{Op: opcode.Not, V: $2} } | '~' SimpleExpr %prec neg { $$ = &ast.UnaryOperationExpr{Op: opcode.BitNeg, V: $2} } | '-' SimpleExpr %prec neg { $$ = &ast.UnaryOperationExpr{Op: opcode.Minus, V: $2} } | '+' SimpleExpr %prec neg { $$ = &ast.UnaryOperationExpr{Op: opcode.Plus, V: $2} } | SimpleExpr pipes SimpleExpr { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.Concat), Args: []ast.ExprNode{$1, $3}} } | not2 SimpleExpr %prec neg { $$ = &ast.UnaryOperationExpr{Op: opcode.Not, V: $2} } | SubSelect | '(' Expression ')' { startOffset := parser.startOffset(&yyS[yypt-1]) endOffset := parser.endOffset(&yyS[yypt]) expr := $2 expr.SetText(parser.src[startOffset:endOffset]) $$ = &ast.ParenthesesExpr{Expr: expr} } | '(' ExpressionList ',' Expression ')' { values := append($2.([]ast.ExprNode), $4) $$ = &ast.RowExpr{Values: values} } | "ROW" '(' ExpressionList ',' Expression ')' { values := append($3.([]ast.ExprNode), $5) $$ = &ast.RowExpr{Values: values} } | "EXISTS" SubSelect { sq := $2.(*ast.SubqueryExpr) sq.Exists = true $$ = &ast.ExistsSubqueryExpr{Sel: sq} } | "BINARY" SimpleExpr %prec neg { // See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#operator_binary x := types.NewFieldType(mysql.TypeString) x.Charset = charset.CharsetBin x.Collate = charset.CharsetBin $$ = &ast.FuncCastExpr{ Expr: $2, Tp: x, FunctionType: ast.CastBinaryOperator, } } | builtinCast '(' Expression "AS" CastType ')' { /* See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_cast */ tp := $5.(*types.FieldType) defaultFlen, defaultDecimal := mysql.GetDefaultFieldLengthAndDecimalForCast(tp.Tp) if tp.Flen == types.UnspecifiedLength { tp.Flen = defaultFlen } if tp.Decimal == types.UnspecifiedLength { tp.Decimal = defaultDecimal } $$ = &ast.FuncCastExpr{ Expr: $3, Tp: tp, FunctionType: ast.CastFunction, } } | "CASE" ExpressionOpt WhenClauseList ElseOpt "END" { x := &ast.CaseExpr{WhenClauses: $3.([]*ast.WhenClause)} if $2 != nil { x.Value = $2 } if $4 != nil { x.ElseClause = $4.(ast.ExprNode) } $$ = x } | "CONVERT" '(' Expression ',' CastType ')' { // See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert tp := $5.(*types.FieldType) defaultFlen, defaultDecimal := mysql.GetDefaultFieldLengthAndDecimalForCast(tp.Tp) if tp.Flen == types.UnspecifiedLength { tp.Flen = defaultFlen } if tp.Decimal == types.UnspecifiedLength { tp.Decimal = defaultDecimal } $$ = &ast.FuncCastExpr{ Expr: $3, Tp: tp, FunctionType: ast.CastConvertFunction, } } | "CONVERT" '(' Expression "USING" CharsetName ')' { // See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert charset1 := ast.NewValueExpr($5) $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3, charset1}, } } | "DEFAULT" '(' SimpleIdent ')' { $$ = &ast.DefaultExpr{Name: $3.(*ast.ColumnNameExpr).Name} } | "VALUES" '(' SimpleIdent ')' %prec lowerThanInsertValues { $$ = &ast.ValuesExpr{Column: $3.(*ast.ColumnNameExpr)} } | SimpleIdent jss stringLit { expr := ast.NewValueExpr($3) $$ = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.JSONExtract), Args: []ast.ExprNode{$1, expr}} } | SimpleIdent juss stringLit { expr := ast.NewValueExpr($3) extract := &ast.FuncCallExpr{FnName: model.NewCIStr(ast.JSONExtract), Args: []ast.ExprNode{$1, expr}} $$ = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.JSONUnquote), Args: []ast.ExprNode{extract}} } DistinctKwd: "DISTINCT" | "DISTINCTROW" DistinctOpt: "ALL" { $$ = false } | DistinctKwd { $$ = true } DefaultFalseDistinctOpt: { $$ = false } | DistinctOpt DefaultTrueDistinctOpt: { $$ = true } | DistinctOpt BuggyDefaultFalseDistinctOpt: DefaultFalseDistinctOpt | DistinctKwd "ALL" { $$ = true } FunctionNameConflict: "ASCII" | "CHARSET" | "COALESCE" | "COLLATION" | "DATE" | "DATABASE" | "DAY" | "HOUR" | "IF" | "INTERVAL" %prec lowerThanIntervalKeyword | "FORMAT" | "LEFT" | "MICROSECOND" | "MINUTE" | "MONTH" | builtinNow | "QUARTER" | "REPEAT" | "REPLACE" | "REVERSE" | "RIGHT" | "ROW_COUNT" | "SECOND" | "TIME" | "TIMESTAMP" | "TRUNCATE" | "USER" | "WEEK" | "YEAR" OptionalBraces: {} | '(' ')' {} FunctionNameOptionalBraces: "CURRENT_USER" | "CURRENT_DATE" | "CURRENT_ROLE" | "UTC_DATE" FunctionNameDatetimePrecision: "CURRENT_TIME" | "CURRENT_TIMESTAMP" | "LOCALTIME" | "LOCALTIMESTAMP" | "UTC_TIME" | "UTC_TIMESTAMP" FunctionCallKeyword: FunctionNameConflict '(' ExpressionListOpt ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} } | builtinUser '(' ExpressionListOpt ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} } | FunctionNameOptionalBraces OptionalBraces { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1)} } | builtinCurDate '(' ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1)} } | FunctionNameDatetimePrecision FuncDatetimePrec { args := []ast.ExprNode{} if $2 != nil { args = append(args, $2.(ast.ExprNode)) } $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: args} } | "CHAR" '(' ExpressionList ')' { nilVal := ast.NewValueExpr(nil) args := $3.([]ast.ExprNode) $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr(ast.CharFunc), Args: append(args, nilVal), } } | "CHAR" '(' ExpressionList "USING" CharsetName ')' { charset1 := ast.NewValueExpr($5) args := $3.([]ast.ExprNode) $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr(ast.CharFunc), Args: append(args, charset1), } } | "DATE" stringLit { expr := ast.NewValueExpr($2) $$ = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.DateLiteral), Args: []ast.ExprNode{expr}} } | "TIME" stringLit { expr := ast.NewValueExpr($2) $$ = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.TimeLiteral), Args: []ast.ExprNode{expr}} } | "TIMESTAMP" stringLit { expr := ast.NewValueExpr($2) $$ = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.TimestampLiteral), Args: []ast.ExprNode{expr}} } | "INSERT" '(' ExpressionListOpt ')' { $$ = &ast.FuncCallExpr{FnName:model.NewCIStr(ast.InsertFunc), Args: $3.([]ast.ExprNode)} } | "MOD" '(' BitExpr ',' BitExpr ')' { $$ = &ast.BinaryOperationExpr{Op: opcode.Mod, L: $3, R: $5} } | "PASSWORD" '(' ExpressionListOpt ')' { $$ = &ast.FuncCallExpr{FnName:model.NewCIStr(ast.PasswordFunc), Args: $3.([]ast.ExprNode)} } | '{' ODBCDateTimeType stringLit '}' { // This is ODBC syntax for date and time literals. // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html expr := ast.NewValueExpr($3) $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($2), Args: []ast.ExprNode{expr}} } FunctionCallNonKeyword: builtinCurTime '(' FuncDatetimePrecListOpt ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} } | builtinSysDate '(' FuncDatetimePrecListOpt ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} } | FunctionNameDateArithMultiForms '(' Expression ',' Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{ $3, $5, &ast.TimeUnitExpr{Unit: ast.TimeUnitDay}, }, } } | FunctionNameDateArithMultiForms '(' Expression ',' "INTERVAL" Expression TimeUnit ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{ $3, $6, &ast.TimeUnitExpr{Unit: $7.(ast.TimeUnitType)}, }, } } | FunctionNameDateArith '(' Expression ',' "INTERVAL" Expression TimeUnit ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{ $3, $6, &ast.TimeUnitExpr{Unit: $7.(ast.TimeUnitType)}, }, } } | builtinExtract '(' TimeUnit "FROM" Expression ')' { timeUnit := &ast.TimeUnitExpr{Unit: $3.(ast.TimeUnitType)} $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{timeUnit, $5}, } } | "GET_FORMAT" '(' GetFormatSelector ',' Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{ &ast.GetFormatSelectorExpr{Selector: $3.(ast.GetFormatSelectorType)}, $5, }, } } | builtinPosition '(' BitExpr "IN" Expression ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3, $5}} } | builtinSubstring '(' Expression ',' Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3, $5}, } } | builtinSubstring '(' Expression "FROM" Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3, $5}, } } | builtinSubstring '(' Expression ',' Expression ',' Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3, $5, $7}, } } | builtinSubstring '(' Expression "FROM" Expression "FOR" Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3, $5, $7}, } } | "TIMESTAMPADD" '(' TimestampUnit ',' Expression ',' Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{&ast.TimeUnitExpr{Unit: $3.(ast.TimeUnitType)}, $5, $7}, } } | "TIMESTAMPDIFF" '(' TimestampUnit ',' Expression ',' Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{&ast.TimeUnitExpr{Unit: $3.(ast.TimeUnitType)}, $5, $7}, } } | builtinTrim '(' Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3}, } } | builtinTrim '(' Expression "FROM" Expression ')' { $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$5, $3}, } } | builtinTrim '(' TrimDirection "FROM" Expression ')' { nilVal := ast.NewValueExpr(nil) direction := &ast.TrimDirectionExpr{Direction: $3.(ast.TrimDirectionType)} $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$5, nilVal, direction}, } } | builtinTrim '(' TrimDirection Expression "FROM" Expression ')' { direction := &ast.TrimDirectionExpr{Direction: $3.(ast.TrimDirectionType)} $$ = &ast.FuncCallExpr{ FnName: model.NewCIStr($1), Args: []ast.ExprNode{$6, $4, direction}, } } GetFormatSelector: "DATE" { $$ = ast.GetFormatSelectorDate } | "DATETIME" { $$ = ast.GetFormatSelectorDatetime } | "TIME" { $$ = ast.GetFormatSelectorTime } | "TIMESTAMP" { $$ = ast.GetFormatSelectorDatetime } FunctionNameDateArith: builtinDateAdd | builtinDateSub FunctionNameDateArithMultiForms: builtinAddDate | builtinSubDate TrimDirection: "BOTH" { $$ = ast.TrimBoth } | "LEADING" { $$ = ast.TrimLeading } | "TRAILING" { $$ = ast.TrimTrailing } SumExpr: "AVG" '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause { if $6 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool), Spec: *($6.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } } | builtinBitAnd '(' Expression ')' OptWindowingClause { if $5 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$3}, Spec: *($5.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3},} } } | builtinBitAnd '(' "ALL" Expression ')' OptWindowingClause { if $6 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Spec: *($6.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4},} } } | builtinBitOr '(' Expression ')' OptWindowingClause { if $5 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$3}, Spec: *($5.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3},} } } | builtinBitOr '(' "ALL" Expression ')' OptWindowingClause { if $6 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Spec: *($6.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4},} } } | builtinBitXor '(' Expression ')' OptWindowingClause { if $5 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$3}, Spec: *($5.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3},} } } | builtinBitXor '(' "ALL" Expression ')' OptWindowingClause { if $6 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Spec: *($6.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4},} } } | builtinCount '(' DistinctKwd ExpressionList ')' { $$ = &ast.AggregateFuncExpr{F: $1, Args: $4.([]ast.ExprNode), Distinct: true} } | builtinCount '(' "ALL" Expression ')' OptWindowingClause { if $6 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Spec: *($6.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4},} } } | builtinCount '(' Expression ')' OptWindowingClause { if $5 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$3}, Spec: *($5.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3},} } } | builtinCount '(' '*' ')' OptWindowingClause { args := []ast.ExprNode{ast.NewValueExpr(1)} if $5 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: args, Spec: *($5.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: args,} } } | builtinGroupConcat '(' BuggyDefaultFalseDistinctOpt ExpressionList OrderByOptional OptGConcatSeparator ')' { args := $4.([]ast.ExprNode) args = append(args, $6.(ast.ExprNode)) $$ = &ast.AggregateFuncExpr{F: $1, Args: args, Distinct: $3.(bool)} } | builtinMax '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause { if $6 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool), Spec: *($6.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } } | builtinMin '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause { if $6 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool), Spec: *($6.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } } | builtinSum '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause { if $6 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool), Spec: *($6.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } } | builtinStddevPop '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause { if $6 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool), Spec: *($6.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } } | builtinStddevSamp '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause { if $6 != nil { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool), Spec: *($6.(*ast.WindowSpec)),} } else { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } } | builtinVarPop '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause { $$ = &ast.AggregateFuncExpr{F: ast.AggFuncVarPop, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } | builtinVarSamp '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause { $$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)} } OptGConcatSeparator: { $$ = ast.NewValueExpr(",") } | "SEPARATOR" stringLit { $$ = ast.NewValueExpr($2) } FunctionCallGeneric: identifier '(' ExpressionListOpt ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)} } FuncDatetimePrec: { $$ = nil } | '(' ')' { $$ = nil } | '(' intLit ')' { expr := ast.NewValueExpr($2) $$ = expr } TimeUnit: TimestampUnit { $$ = $1 } | "SECOND_MICROSECOND" { $$ = ast.TimeUnitSecondMicrosecond } | "MINUTE_MICROSECOND" { $$ = ast.TimeUnitMinuteMicrosecond } | "MINUTE_SECOND" { $$ = ast.TimeUnitMinuteSecond } | "HOUR_MICROSECOND" { $$ = ast.TimeUnitHourMicrosecond } | "HOUR_SECOND" { $$ = ast.TimeUnitHourSecond } | "HOUR_MINUTE" { $$ = ast.TimeUnitHourMinute } | "DAY_MICROSECOND" { $$ = ast.TimeUnitDayMicrosecond } | "DAY_SECOND" { $$ = ast.TimeUnitDaySecond } | "DAY_MINUTE" { $$ = ast.TimeUnitDayMinute } | "DAY_HOUR" { $$ = ast.TimeUnitDayHour } | "YEAR_MONTH" { $$ = ast.TimeUnitYearMonth } TimestampUnit: "MICROSECOND" { $$ = ast.TimeUnitMicrosecond } | "SECOND" { $$ = ast.TimeUnitSecond } | "MINUTE" { $$ = ast.TimeUnitMinute } | "HOUR" { $$ = ast.TimeUnitHour } | "DAY" { $$ = ast.TimeUnitDay } | "WEEK" { $$ = ast.TimeUnitWeek } | "MONTH" { $$ = ast.TimeUnitMonth } | "QUARTER" { $$ = ast.TimeUnitQuarter } | "YEAR" { $$ = ast.TimeUnitYear } | "SQL_TSI_SECOND" { $$ = ast.TimeUnitSecond } | "SQL_TSI_MINUTE" { $$ = ast.TimeUnitMinute } | "SQL_TSI_HOUR" { $$ = ast.TimeUnitHour } | "SQL_TSI_DAY" { $$ = ast.TimeUnitDay } | "SQL_TSI_WEEK" { $$ = ast.TimeUnitWeek } | "SQL_TSI_MONTH" { $$ = ast.TimeUnitMonth } | "SQL_TSI_QUARTER" { $$ = ast.TimeUnitQuarter } | "SQL_TSI_YEAR" { $$ = ast.TimeUnitYear } ExpressionOpt: { $$ = nil } | Expression { $$ = $1 } WhenClauseList: WhenClause { $$ = []*ast.WhenClause{$1.(*ast.WhenClause)} } | WhenClauseList WhenClause { $$ = append($1.([]*ast.WhenClause), $2.(*ast.WhenClause)) } WhenClause: "WHEN" Expression "THEN" Expression { $$ = &ast.WhenClause{ Expr: $2, Result: $4, } } ElseOpt: /* empty */ { $$ = nil } | "ELSE" Expression { $$ = $2 } CastType: "BINARY" OptFieldLen { x := types.NewFieldType(mysql.TypeVarString) x.Flen = $2.(int) // TODO: Flen should be the flen of expression if x.Flen != types.UnspecifiedLength { x.Tp = mysql.TypeString } x.Charset = charset.CharsetBin x.Collate = charset.CollationBin x.Flag |= mysql.BinaryFlag $$ = x } | "CHAR" OptFieldLen OptBinary { x := types.NewFieldType(mysql.TypeVarString) x.Flen = $2.(int) // TODO: Flen should be the flen of expression x.Charset = $3.(*ast.OptBinary).Charset if $3.(*ast.OptBinary).IsBinary{ x.Flag |= mysql.BinaryFlag } if x.Charset == "" { x.Charset = mysql.DefaultCharset x.Collate = mysql.DefaultCollationName } $$ = x } | "DATE" { x := types.NewFieldType(mysql.TypeDate) x.Charset = charset.CharsetBin x.Collate = charset.CollationBin x.Flag |= mysql.BinaryFlag $$ = x } | "DATETIME" OptFieldLen { x := types.NewFieldType(mysql.TypeDatetime) x.Flen, _ = mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDatetime) x.Decimal = $2.(int) if x.Decimal > 0 { x.Flen = x.Flen + 1 + x.Decimal } x.Charset = charset.CharsetBin x.Collate = charset.CollationBin x.Flag |= mysql.BinaryFlag $$ = x } | "DECIMAL" FloatOpt { fopt := $2.(*ast.FloatOpt) x := types.NewFieldType(mysql.TypeNewDecimal) x.Flen = fopt.Flen x.Decimal = fopt.Decimal x.Charset = charset.CharsetBin x.Collate = charset.CollationBin x.Flag |= mysql.BinaryFlag $$ = x } | "TIME" OptFieldLen { x := types.NewFieldType(mysql.TypeDuration) x.Flen, _ = mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDuration) x.Decimal = $2.(int) if x.Decimal > 0 { x.Flen = x.Flen + 1 + x.Decimal } x.Charset = charset.CharsetBin x.Collate = charset.CollationBin x.Flag |= mysql.BinaryFlag $$ = x } | "SIGNED" OptInteger { x := types.NewFieldType(mysql.TypeLonglong) x.Charset = charset.CharsetBin x.Collate = charset.CollationBin x.Flag |= mysql.BinaryFlag $$ = x } | "UNSIGNED" OptInteger { x := types.NewFieldType(mysql.TypeLonglong) x.Flag |= mysql.UnsignedFlag | mysql.BinaryFlag x.Charset = charset.CharsetBin x.Collate = charset.CollationBin $$ = x } | "JSON" { x := types.NewFieldType(mysql.TypeJSON) x.Flag |= mysql.BinaryFlag | (mysql.ParseToJSONFlag) x.Charset = mysql.DefaultCharset x.Collate = mysql.DefaultCollationName $$ = x } | "DOUBLE" { x := types.NewFieldType(mysql.TypeDouble) x.Flen, x.Decimal = mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDouble) x.Flag |= mysql.BinaryFlag x.Charset = charset.CharsetBin x.Collate = charset.CollationBin $$ = x } | "FLOAT" FloatOpt { x := types.NewFieldType(mysql.TypeFloat) fopt := $2.(*ast.FloatOpt) if fopt.Flen >= 54 { yylex.AppendError(ErrTooBigPrecision.GenWithStackByArgs(fopt.Flen,"CAST",53)) } else if fopt.Flen >= 25 { x = types.NewFieldType(mysql.TypeDouble) } x.Flen, x.Decimal = mysql.GetDefaultFieldLengthAndDecimalForCast(x.Tp) x.Flag |= mysql.BinaryFlag x.Charset = charset.CharsetBin x.Collate = charset.CollationBin $$ = x } | "REAL" { var x *types.FieldType if parser.lexer.GetSQLMode().HasRealAsFloatMode() { x = types.NewFieldType(mysql.TypeFloat) } else { x = types.NewFieldType(mysql.TypeDouble) } x.Flen, x.Decimal = mysql.GetDefaultFieldLengthAndDecimalForCast(x.Tp) x.Flag |= mysql.BinaryFlag x.Charset = charset.CharsetBin x.Collate = charset.CollationBin $$ = x } PriorityOpt: { $$ = mysql.NoPriority } | "LOW_PRIORITY" { $$ = mysql.LowPriority } | "HIGH_PRIORITY" { $$ = mysql.HighPriority } | "DELAYED" { $$ = mysql.DelayedPriority } TableName: Identifier { $$ = &ast.TableName{Name:model.NewCIStr($1)} } | Identifier '.' Identifier { $$ = &ast.TableName{Schema:model.NewCIStr($1), Name:model.NewCIStr($3)} } | Identifier '.' '*' { $$ = &ast.TableName{Name:model.NewCIStr($1)} } TableNameList: TableName { tbl := []*ast.TableName{$1.(*ast.TableName)} $$ = tbl } | TableNameList ',' TableName { $$ = append($1.([]*ast.TableName), $3.(*ast.TableName)) } QuickOptional: %prec empty { $$ = false } | "QUICK" { $$ = true } /***************************Prepared Statement Start****************************** * See https://dev.mysql.com/doc/refman/5.7/en/prepare.html * Example: * PREPARE stmt_name FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse'; * OR * SET @s = 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse'; * PREPARE stmt_name FROM @s; */ PreparedStmt: "PREPARE" Identifier "FROM" PrepareSQL { var sqlText string var sqlVar *ast.VariableExpr switch $4.(type) { case string: sqlText = $4.(string) case *ast.VariableExpr: sqlVar = $4.(*ast.VariableExpr) } $$ = &ast.PrepareStmt{ Name: $2, SQLText: sqlText, SQLVar: sqlVar, } } PrepareSQL: stringLit { $$ = $1 } | UserVariable { $$ = $1.(interface{}) } /* * See https://dev.mysql.com/doc/refman/5.7/en/execute.html * Example: * EXECUTE stmt1 USING @a, @b; * OR * EXECUTE stmt1; */ ExecuteStmt: "EXECUTE" Identifier { $$ = &ast.ExecuteStmt{Name: $2} } | "EXECUTE" Identifier "USING" UserVariableList { $$ = &ast.ExecuteStmt{ Name: $2, UsingVars: $4.([]ast.ExprNode), } } UserVariableList: UserVariable { $$ = []ast.ExprNode{$1} } | UserVariableList ',' UserVariable { $$ = append($1.([]ast.ExprNode), $3) } /* * See https://dev.mysql.com/doc/refman/5.0/en/deallocate-prepare.html */ DeallocateStmt: DeallocateSym "PREPARE" Identifier { $$ = &ast.DeallocateStmt{Name: $3} } DeallocateSym: "DEALLOCATE" | "DROP" /****************************Prepared Statement End*******************************/ RollbackStmt: "ROLLBACK" { $$ = &ast.RollbackStmt{} } ShutdownStmt: "SHUTDOWN" { $$ = &ast.ShutdownStmt{} } SelectStmtBasic: "SELECT" SelectStmtOpts SelectStmtFieldList { st := &ast.SelectStmt { SelectStmtOpts: $2.(*ast.SelectStmtOpts), Distinct: $2.(*ast.SelectStmtOpts).Distinct, Fields: $3.(*ast.FieldList), } if st.SelectStmtOpts.TableHints != nil { st.TableHints = st.SelectStmtOpts.TableHints } $$ = st } SelectStmtFromDualTable: SelectStmtBasic FromDual WhereClauseOptional { st := $1.(*ast.SelectStmt) lastField := st.Fields.Fields[len(st.Fields.Fields)-1] if lastField.Expr != nil && lastField.AsName.O == "" { lastEnd := yyS[yypt-1].offset-1 lastField.SetText(parser.src[lastField.Offset:lastEnd]) } if $3 != nil { st.Where = $3.(ast.ExprNode) } } SelectStmtFromTable: SelectStmtBasic "FROM" TableRefsClause WhereClauseOptional SelectStmtGroup HavingClause WindowClauseOptional { st := $1.(*ast.SelectStmt) st.From = $3.(*ast.TableRefsClause) lastField := st.Fields.Fields[len(st.Fields.Fields)-1] if lastField.Expr != nil && lastField.AsName.O == "" { lastEnd := parser.endOffset(&yyS[yypt-5]) lastField.SetText(parser.src[lastField.Offset:lastEnd]) } if $4 != nil { st.Where = $4.(ast.ExprNode) } if $5 != nil { st.GroupBy = $5.(*ast.GroupByClause) } if $6 != nil { st.Having = $6.(*ast.HavingClause) } if $7 != nil { st.WindowSpecs = ($7.([]ast.WindowSpec)) } $$ = st } SelectStmt: SelectStmtBasic OrderByOptional SelectStmtLimit SelectLockOpt { st := $1.(*ast.SelectStmt) st.LockTp = $4.(ast.SelectLockType) lastField := st.Fields.Fields[len(st.Fields.Fields)-1] if lastField.Expr != nil && lastField.AsName.O == "" { src := parser.src var lastEnd int if $2 != nil { lastEnd = yyS[yypt-2].offset-1 } else if $3 != nil { lastEnd = yyS[yypt-1].offset-1 } else if $4 != ast.SelectLockNone { lastEnd = yyS[yypt].offset-1 } else { lastEnd = len(src) if src[lastEnd-1] == ';' { lastEnd-- } } lastField.SetText(src[lastField.Offset:lastEnd]) } if $2 != nil { st.OrderBy = $2.(*ast.OrderByClause) } if $3 != nil { st.Limit = $3.(*ast.Limit) } $$ = st } | SelectStmtFromDualTable OrderByOptional SelectStmtLimit SelectLockOpt { st := $1.(*ast.SelectStmt) if $2 != nil { st.OrderBy = $2.(*ast.OrderByClause) } if $3 != nil { st.Limit = $3.(*ast.Limit) } st.LockTp = $4.(ast.SelectLockType) $$ = st } | SelectStmtFromTable OrderByOptional SelectStmtLimit SelectLockOpt { st := $1.(*ast.SelectStmt) st.LockTp = $4.(ast.SelectLockType) if $2 != nil { st.OrderBy = $2.(*ast.OrderByClause) } if $3 != nil { st.Limit = $3.(*ast.Limit) } $$ = st } FromDual: "FROM" "DUAL" WindowClauseOptional: { $$ = nil } | "WINDOW" WindowDefinitionList { $$ = $2.([]ast.WindowSpec) } WindowDefinitionList: WindowDefinition { $$ = []ast.WindowSpec{$1.(ast.WindowSpec)} } | WindowDefinitionList ',' WindowDefinition { $$ = append($1.([]ast.WindowSpec), $3.(ast.WindowSpec)) } WindowDefinition: WindowName "AS" WindowSpec { var spec = $3.(ast.WindowSpec) spec.Name = $1.(model.CIStr) $$ = spec } WindowName: Identifier { $$ = model.NewCIStr($1) } WindowSpec: '(' WindowSpecDetails ')' { $$ = $2.(ast.WindowSpec) } WindowSpecDetails: OptExistingWindowName OptPartitionClause OptWindowOrderByClause OptWindowFrameClause { spec := ast.WindowSpec{Ref: $1.(model.CIStr),} if $2 != nil { spec.PartitionBy = $2.(*ast.PartitionByClause) } if $3 != nil { spec.OrderBy = $3.(*ast.OrderByClause) } if $4 != nil { spec.Frame = $4.(*ast.FrameClause) } $$ = spec } OptExistingWindowName: { $$ = model.CIStr{} } | WindowName { $$ = $1.(model.CIStr) } OptPartitionClause: { $$ = nil } | "PARTITION" "BY" ByList { $$ = &ast.PartitionByClause{Items: $3.([]*ast.ByItem)} } OptWindowOrderByClause: { $$ = nil } | "ORDER" "BY" ByList { $$ = &ast.OrderByClause{Items: $3.([]*ast.ByItem)} } OptWindowFrameClause: { $$ = nil } | WindowFrameUnits WindowFrameExtent { $$ = &ast.FrameClause{ Type: $1.(ast.FrameType), Extent: $2.(ast.FrameExtent), } } WindowFrameUnits: "ROWS" { $$ = ast.FrameType(ast.Rows) } | "RANGE" { $$ = ast.FrameType(ast.Ranges) } | "GROUPS" { $$ = ast.FrameType(ast.Groups) } WindowFrameExtent: WindowFrameStart { $$ = ast.FrameExtent { Start: $1.(ast.FrameBound), End: ast.FrameBound{Type: ast.CurrentRow,}, } } | WindowFrameBetween { $$ = $1.(ast.FrameExtent) } WindowFrameStart: "UNBOUNDED" "PRECEDING" { $$ = ast.FrameBound{Type: ast.Preceding, UnBounded: true,} } | NumLiteral "PRECEDING" { $$ = ast.FrameBound{Type: ast.Preceding, Expr: ast.NewValueExpr($1),} } | paramMarker "PRECEDING" { $$ = ast.FrameBound{Type: ast.Preceding, Expr: ast.NewParamMarkerExpr(yyS[yypt].offset),} } | "INTERVAL" Expression TimeUnit "PRECEDING" { $$ = ast.FrameBound{Type: ast.Preceding, Expr: $2, Unit: $3.(ast.TimeUnitType),} } | "CURRENT" "ROW" { $$ = ast.FrameBound{Type: ast.CurrentRow,} } WindowFrameBetween: "BETWEEN" WindowFrameBound "AND" WindowFrameBound { $$ = ast.FrameExtent{Start: $2.(ast.FrameBound), End: $4.(ast.FrameBound),} } WindowFrameBound: WindowFrameStart { $$ = $1.(ast.FrameBound) } | "UNBOUNDED" "FOLLOWING" { $$ = ast.FrameBound{Type: ast.Following, UnBounded: true,} } | NumLiteral "FOLLOWING" { $$ = ast.FrameBound{Type: ast.Following, Expr: ast.NewValueExpr($1),} } | paramMarker "FOLLOWING" { $$ = ast.FrameBound{Type: ast.Following, Expr: ast.NewParamMarkerExpr(yyS[yypt].offset),} } | "INTERVAL" Expression TimeUnit "FOLLOWING" { $$ = ast.FrameBound{Type: ast.Following, Expr: $2, Unit: $3.(ast.TimeUnitType),} } OptWindowingClause: { $$ = nil } | WindowingClause { spec := $1.(ast.WindowSpec) $$ = &spec } WindowingClause: "OVER" WindowNameOrSpec { $$ = $2.(ast.WindowSpec) } WindowNameOrSpec: WindowName { $$ = ast.WindowSpec{Name: $1.(model.CIStr), OnlyAlias: true,} } | WindowSpec { $$ = $1.(ast.WindowSpec) } WindowFuncCall: "ROW_NUMBER" '(' ')' WindowingClause { $$ = &ast.WindowFuncExpr{F: $1, Spec: $4.(ast.WindowSpec),} } | "RANK" '(' ')' WindowingClause { $$ = &ast.WindowFuncExpr{F: $1, Spec: $4.(ast.WindowSpec),} } | "DENSE_RANK" '(' ')' WindowingClause { $$ = &ast.WindowFuncExpr{F: $1, Spec: $4.(ast.WindowSpec),} } | "CUME_DIST" '(' ')' WindowingClause { $$ = &ast.WindowFuncExpr{F: $1, Spec: $4.(ast.WindowSpec),} } | "PERCENT_RANK" '(' ')' WindowingClause { $$ = &ast.WindowFuncExpr{F: $1, Spec: $4.(ast.WindowSpec),} } | "NTILE" '(' SimpleExpr ')' WindowingClause { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$3}, Spec: $5.(ast.WindowSpec),} } | "LEAD" '(' Expression OptLeadLagInfo ')' OptNullTreatment WindowingClause { args := []ast.ExprNode{$3} if $4 != nil { args = append(args, $4.([]ast.ExprNode)...) } $$ = &ast.WindowFuncExpr{F: $1, Args: args, IgnoreNull: $6.(bool), Spec: $7.(ast.WindowSpec),} } | "LAG" '(' Expression OptLeadLagInfo ')' OptNullTreatment WindowingClause { args := []ast.ExprNode{$3} if $4 != nil { args = append(args, $4.([]ast.ExprNode)...) } $$ = &ast.WindowFuncExpr{F: $1, Args: args, IgnoreNull: $6.(bool), Spec: $7.(ast.WindowSpec),} } | "FIRST_VALUE" '(' Expression ')' OptNullTreatment WindowingClause { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$3}, IgnoreNull: $5.(bool), Spec: $6.(ast.WindowSpec),} } | "LAST_VALUE" '(' Expression ')' OptNullTreatment WindowingClause { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$3}, IgnoreNull: $5.(bool), Spec: $6.(ast.WindowSpec),} } | "NTH_VALUE" '(' Expression ',' SimpleExpr ')' OptFromFirstLast OptNullTreatment WindowingClause { $$ = &ast.WindowFuncExpr{F: $1, Args: []ast.ExprNode{$3, $5}, FromLast: $7.(bool), IgnoreNull: $8.(bool), Spec: $9.(ast.WindowSpec),} } OptLeadLagInfo: { $$ = nil } | ',' NumLiteral OptLLDefault { args := []ast.ExprNode{ast.NewValueExpr($2)} if $3 != nil { args = append(args, $3.(ast.ExprNode)) } $$ = args } | ',' paramMarker OptLLDefault { args := []ast.ExprNode{ast.NewValueExpr($2)} if $3 != nil { args = append(args, $3.(ast.ExprNode)) } $$ = args } OptLLDefault: { $$ = nil } | ',' Expression { $$ = $2 } OptNullTreatment: { $$ = false } | "RESPECT" "NULLS" { $$ = false } | "IGNORE" "NULLS" { $$ = true } OptFromFirstLast: { $$ = false } | "FROM" "FIRST" { $$ = false } | "FROM" "LAST" { $$ = true } TableRefsClause: TableRefs { $$ = &ast.TableRefsClause{TableRefs: $1.(*ast.Join)} } TableRefs: EscapedTableRef { if j, ok := $1.(*ast.Join); ok { // if $1 is Join, use it directly $$ = j } else { $$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: nil} } } | TableRefs ',' EscapedTableRef { /* from a, b is default cross join */ $$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $3.(ast.ResultSetNode), Tp: ast.CrossJoin} } EscapedTableRef: TableRef %prec lowerThanSetKeyword { $$ = $1 } | '{' Identifier TableRef '}' { /* * ODBC escape syntax for outer join is { OJ join_table } * Use an Identifier for OJ */ $$ = $3 } TableRef: TableFactor { $$ = $1 } | JoinTable { $$ = $1 } TableFactor: TableName PartitionNameListOpt TableAsNameOpt IndexHintListOpt { tn := $1.(*ast.TableName) tn.PartitionNames = $2.([]model.CIStr) tn.IndexHints = $4.([]*ast.IndexHint) $$ = &ast.TableSource{Source: tn, AsName: $3.(model.CIStr)} } | '(' SelectStmt ')' TableAsName { st := $2.(*ast.SelectStmt) endOffset := parser.endOffset(&yyS[yypt-1]) parser.setLastSelectFieldText(st, endOffset) $$ = &ast.TableSource{Source: $2.(*ast.SelectStmt), AsName: $4.(model.CIStr)} } | '(' UnionStmt ')' TableAsName { $$ = &ast.TableSource{Source: $2.(*ast.UnionStmt), AsName: $4.(model.CIStr)} } | '(' TableRefs ')' { $$ = $2 } PartitionNameListOpt: /* empty */ { $$ = []model.CIStr{} } | "PARTITION" '(' PartitionNameList ')' { $$ = $3 } TableAsNameOpt: { $$ = model.CIStr{} } | TableAsName { $$ = $1 } TableAsName: Identifier { $$ = model.NewCIStr($1) } | "AS" Identifier { $$ = model.NewCIStr($2) } IndexHintType: "USE" KeyOrIndex { $$ = ast.HintUse } | "IGNORE" KeyOrIndex { $$ = ast.HintIgnore } | "FORCE" KeyOrIndex { $$ = ast.HintForce } IndexHintScope: { $$ = ast.HintForScan } | "FOR" "JOIN" { $$ = ast.HintForJoin } | "FOR" "ORDER" "BY" { $$ = ast.HintForOrderBy } | "FOR" "GROUP" "BY" { $$ = ast.HintForGroupBy } IndexHint: IndexHintType IndexHintScope '(' IndexNameList ')' { $$ = &ast.IndexHint{ IndexNames: $4.([]model.CIStr), HintType: $1.(ast.IndexHintType), HintScope: $2.(ast.IndexHintScope), } } IndexNameList: { var nameList []model.CIStr $$ = nameList } | Identifier { $$ = []model.CIStr{model.NewCIStr($1)} } | IndexNameList ',' Identifier { $$ = append($1.([]model.CIStr), model.NewCIStr($3)) } | "PRIMARY" { $$ = []model.CIStr{model.NewCIStr($1)} } IndexHintList: IndexHint { $$ = []*ast.IndexHint{$1.(*ast.IndexHint)} } | IndexHintList IndexHint { $$ = append($1.([]*ast.IndexHint), $2.(*ast.IndexHint)) } IndexHintListOpt: { var hintList []*ast.IndexHint $$ = hintList } | IndexHintList { $$ = $1 } JoinTable: /* Use %prec to evaluate production TableRef before cross join */ TableRef CrossOpt TableRef %prec tableRefPriority { $$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $3.(ast.ResultSetNode), Tp: ast.CrossJoin} } | TableRef CrossOpt TableRef "ON" Expression { on := &ast.OnCondition{Expr: $5} $$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $3.(ast.ResultSetNode), Tp: ast.CrossJoin, On: on} } | TableRef CrossOpt TableRef "USING" '(' ColumnNameList ')' { $$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $3.(ast.ResultSetNode), Tp: ast.CrossJoin, Using: $6.([]*ast.ColumnName)} } | TableRef JoinType OuterOpt "JOIN" TableRef "ON" Expression { on := &ast.OnCondition{Expr: $7} $$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $5.(ast.ResultSetNode), Tp: $2.(ast.JoinType), On: on} } | TableRef JoinType OuterOpt "JOIN" TableRef "USING" '(' ColumnNameList ')' { $$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $5.(ast.ResultSetNode), Tp: $2.(ast.JoinType), Using: $8.([]*ast.ColumnName)} } | TableRef "NATURAL" "JOIN" TableRef { $$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $4.(ast.ResultSetNode), NaturalJoin: true} } | TableRef "NATURAL" JoinType OuterOpt "JOIN" TableRef { $$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $6.(ast.ResultSetNode), Tp: $3.(ast.JoinType), NaturalJoin: true} } | TableRef "STRAIGHT_JOIN" TableRef { $$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $3.(ast.ResultSetNode), StraightJoin: true} } | TableRef "STRAIGHT_JOIN" TableRef "ON" Expression { on := &ast.OnCondition{Expr: $5} $$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $3.(ast.ResultSetNode), StraightJoin: true, On: on} } JoinType: "LEFT" { $$ = ast.LeftJoin } | "RIGHT" { $$ = ast.RightJoin } OuterOpt: {} | "OUTER" CrossOpt: "JOIN" | "CROSS" "JOIN" | "INNER" "JOIN" LimitClause: { $$ = nil } | "LIMIT" LimitOption { $$ = &ast.Limit{Count: $2.(ast.ValueExpr)} } LimitOption: LengthNum { $$ = ast.NewValueExpr($1) } | paramMarker { $$ = ast.NewParamMarkerExpr(yyS[yypt].offset) } SelectStmtLimit: { $$ = nil } | "LIMIT" LimitOption { $$ = &ast.Limit{Count: $2.(ast.ExprNode)} } | "LIMIT" LimitOption ',' LimitOption { $$ = &ast.Limit{Offset: $2.(ast.ExprNode), Count: $4.(ast.ExprNode)} } | "LIMIT" LimitOption "OFFSET" LimitOption { $$ = &ast.Limit{Offset: $4.(ast.ExprNode), Count: $2.(ast.ExprNode)} } SelectStmtOpts: TableOptimizerHints DefaultFalseDistinctOpt PriorityOpt SelectStmtSQLSmallResult SelectStmtSQLBigResult SelectStmtSQLBufferResult SelectStmtSQLCache SelectStmtCalcFoundRows SelectStmtStraightJoin { opt := &ast.SelectStmtOpts{} if $1 != nil { opt.TableHints = $1.([]*ast.TableOptimizerHint) } if $2 != nil { opt.Distinct = $2.(bool) } if $3 != nil { opt.Priority = $3.(mysql.PriorityEnum) } if $4 != nil { opt.SQLSmallResult = $4.(bool) } if $5 != nil { opt.SQLBigResult = $5.(bool) } if $6 != nil { opt.SQLBufferResult = $6.(bool) } if $7 != nil { opt.SQLCache = $7.(bool) } if $8 != nil { opt.CalcFoundRows = $8.(bool) } if $9 != nil { opt.StraightJoin = $9.(bool) } $$ = opt } TableOptimizerHints: /* empty */ { $$ = nil } | hintBegin OptimizerHintList hintEnd { $$ = $2 } | hintBegin error hintEnd { yyerrok() parser.lastErrorAsWarn() $$ = nil } OptimizerHintList: TableOptimizerHintOpt { $$ = []*ast.TableOptimizerHint{$1.(*ast.TableOptimizerHint)} } | StorageOptimizerHintOpt { $$ = $1.([]*ast.TableOptimizerHint) } | OptimizerHintList TableOptimizerHintOpt { $$ = append($1.([]*ast.TableOptimizerHint), $2.(*ast.TableOptimizerHint)) } | OptimizerHintList ',' TableOptimizerHintOpt { $$ = append($1.([]*ast.TableOptimizerHint), $3.(*ast.TableOptimizerHint)) } | OptimizerHintList StorageOptimizerHintOpt { $$ = append($1.([]*ast.TableOptimizerHint), $2.([]*ast.TableOptimizerHint)...) } | OptimizerHintList ',' StorageOptimizerHintOpt { $$ = append($1.([]*ast.TableOptimizerHint), $3.([]*ast.TableOptimizerHint)...) } TableOptimizerHintOpt: hintUseIndex '(' QueryBlockOpt HintTable IndexNameList ')' { $$ = &ast.TableOptimizerHint{ HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: []ast.HintTable{$4.(ast.HintTable)}, Indexes: $5.([]model.CIStr), } } | hintIgnoreIndex '(' QueryBlockOpt HintTable IndexNameList ')' { $$ = &ast.TableOptimizerHint{ HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: []ast.HintTable{$4.(ast.HintTable)}, Indexes: $5.([]model.CIStr), } } | hintSMJ '(' QueryBlockOpt HintTableList ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: $4.([]ast.HintTable)} } | hintINLJ '(' QueryBlockOpt HintTableList ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: $4.([]ast.HintTable)} } | hintHJ '(' QueryBlockOpt HintTableList ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: $4.([]ast.HintTable)} } | hintUseIndexMerge '(' QueryBlockOpt HintTable IndexNameList ')' { $$ = &ast.TableOptimizerHint{ HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), Tables: []ast.HintTable{$4.(ast.HintTable)}, Indexes: $5.([]model.CIStr), } } | hintUseToja '(' QueryBlockOpt HintTrueOrFalse ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), HintFlag: $4.(bool)} } | hintEnablePlanCache '(' QueryBlockOpt HintTrueOrFalse ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), HintFlag: $4.(bool)} } | maxExecutionTime '(' QueryBlockOpt NUM ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), MaxExecutionTime: getUint64FromNUM($4)} } | hintUsePlanCache '(' QueryBlockOpt ')' { // arguments not decided yet. $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)} } | hintQueryType '(' QueryBlockOpt HintQueryType ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), QueryType: model.NewCIStr($4.(string))} } | hintMemoryQuota '(' QueryBlockOpt HintMemoryQuota ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr), MemoryQuota: $4.(int64)} } | hintHASHAGG '(' QueryBlockOpt ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)} } | hintSTREAMAGG '(' QueryBlockOpt ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)} } | hintAggToCop '(' QueryBlockOpt ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)} } | hintNoIndexMerge '(' QueryBlockOpt ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)} } | hintReadConsistentReplica '(' QueryBlockOpt ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: $3.(model.CIStr)} } | hintQBName '(' Identifier ')' { $$ = &ast.TableOptimizerHint{HintName: model.NewCIStr($1), QBName: model.NewCIStr($3)} } StorageOptimizerHintOpt: hintReadFromStorage '(' QueryBlockOpt HintStorageTypeAndTableList ')' { $$ = $4.([]*ast.TableOptimizerHint) for _, hint := range $$.([]*ast.TableOptimizerHint) { hint.HintName = model.NewCIStr($1) hint.QBName = $3.(model.CIStr) } } HintStorageTypeAndTableList: HintStorageTypeAndTable { $$ = []*ast.TableOptimizerHint{$1.(*ast.TableOptimizerHint)} } | HintStorageTypeAndTableList ',' HintStorageTypeAndTable { $$ = append($1.([]*ast.TableOptimizerHint), $3.(*ast.TableOptimizerHint)) } HintStorageTypeAndTable: HintStorageType '[' HintTableList ']' { $$ = &ast.TableOptimizerHint{ StoreType: model.NewCIStr($1.(string)), Tables: $3.([]ast.HintTable), } } QueryBlockOpt: { $$ = model.NewCIStr("") } | singleAtIdentifier { $$ = model.NewCIStr($1) } HintTable: Identifier QueryBlockOpt { $$ = ast.HintTable{TableName: model.NewCIStr($1), QBName: $2.(model.CIStr)} } HintTableList: HintTable { $$ = []ast.HintTable{$1.(ast.HintTable)} } | HintTableList ',' HintTable { $$ = append($1.([]ast.HintTable), $3.(ast.HintTable)) } HintTrueOrFalse: "TRUE" { $$ = true } | "FALSE" { $$ = false } HintStorageType: hintTiKV { $$ = $1 } | hintTiFlash { $$ = $1 } HintQueryType: hintOLAP { $$ = $1 } | hintOLTP { $$ = $1 } HintMemoryQuota: NUM Identifier { switch model.NewCIStr($2).L { case "mb": $$ = $1.(int64) * 1024 * 1024 case "gb": $$ = $1.(int64) * 1024 * 1024 * 1024 default: // Executor handle memory quota < 0 as no memory limit, here use it to trigger warning in TiDB. $$ = int64(-1) } } SelectStmtCalcFoundRows: { $$ = false } | "SQL_CALC_FOUND_ROWS" { $$ = true } SelectStmtSQLBigResult: %prec empty { $$ = false } | "SQL_BIG_RESULT" { $$ = true } SelectStmtSQLBufferResult: %prec empty { $$ = false } | "SQL_BUFFER_RESULT" { $$ = true } SelectStmtSQLCache: %prec empty { $$ = true } | "SQL_CACHE" { $$ = true } | "SQL_NO_CACHE" { $$ = false } SelectStmtSQLSmallResult: %prec empty { $$ = false } | "SQL_SMALL_RESULT" { $$ = true } SelectStmtStraightJoin: %prec empty { $$ = false } | "STRAIGHT_JOIN" { $$ = true } SelectStmtFieldList: FieldList { $$ = &ast.FieldList{Fields: $1.([]*ast.SelectField)} } SelectStmtGroup: /* EMPTY */ { $$ = nil } | GroupByClause // See https://dev.mysql.com/doc/refman/5.7/en/subqueries.html SubSelect: '(' SelectStmt ')' { s := $2.(*ast.SelectStmt) endOffset := parser.endOffset(&yyS[yypt]) parser.setLastSelectFieldText(s, endOffset) src := parser.src // See the implementation of yyParse function s.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) $$ = &ast.SubqueryExpr{Query: s} } | '(' UnionStmt ')' { s := $2.(*ast.UnionStmt) src := parser.src // See the implementation of yyParse function s.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) $$ = &ast.SubqueryExpr{Query: s} } // See https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html SelectLockOpt: /* empty */ { $$ = ast.SelectLockNone } | "FOR" "UPDATE" { $$ = ast.SelectLockForUpdate } | "FOR" "UPDATE" "NOWAIT" { $$ = ast.SelectLockForUpdateNoWait } | "LOCK" "IN" "SHARE" "MODE" { $$ = ast.SelectLockInShareMode } // See https://dev.mysql.com/doc/refman/5.7/en/union.html UnionStmt: UnionClauseList "UNION" UnionOpt SelectStmtBasic OrderByOptional SelectStmtLimit SelectLockOpt { st := $4.(*ast.SelectStmt) union := $1.(*ast.UnionStmt) st.IsAfterUnionDistinct = $3.(bool) lastSelect := union.SelectList.Selects[len(union.SelectList.Selects)-1] endOffset := parser.endOffset(&yyS[yypt-5]) parser.setLastSelectFieldText(lastSelect, endOffset) union.SelectList.Selects = append(union.SelectList.Selects, st) if $5 != nil { union.OrderBy = $5.(*ast.OrderByClause) } if $6 != nil { union.Limit = $6.(*ast.Limit) } if $5 == nil && $6 == nil { st.LockTp = $7.(ast.SelectLockType) } $$ = union } | UnionClauseList "UNION" UnionOpt SelectStmtFromDualTable OrderByOptional SelectStmtLimit SelectLockOpt { st := $4.(*ast.SelectStmt) union := $1.(*ast.UnionStmt) st.IsAfterUnionDistinct = $3.(bool) lastSelect := union.SelectList.Selects[len(union.SelectList.Selects)-1] endOffset := parser.endOffset(&yyS[yypt-5]) parser.setLastSelectFieldText(lastSelect, endOffset) union.SelectList.Selects = append(union.SelectList.Selects, st) if $5 != nil { union.OrderBy = $5.(*ast.OrderByClause) } if $6 != nil { union.Limit = $6.(*ast.Limit) } if $5 == nil && $6 == nil { st.LockTp = $7.(ast.SelectLockType) } $$ = union } | UnionClauseList "UNION" UnionOpt SelectStmtFromTable OrderByOptional SelectStmtLimit SelectLockOpt { st := $4.(*ast.SelectStmt) union := $1.(*ast.UnionStmt) st.IsAfterUnionDistinct = $3.(bool) lastSelect := union.SelectList.Selects[len(union.SelectList.Selects)-1] endOffset := parser.endOffset(&yyS[yypt-5]) parser.setLastSelectFieldText(lastSelect, endOffset) union.SelectList.Selects = append(union.SelectList.Selects, st) if $5 != nil { union.OrderBy = $5.(*ast.OrderByClause) } if $6 != nil { union.Limit = $6.(*ast.Limit) } if $5 == nil && $6 == nil { st.LockTp = $7.(ast.SelectLockType) } $$ = union } | UnionClauseList "UNION" UnionOpt '(' SelectStmt ')' OrderByOptional SelectStmtLimit { union := $1.(*ast.UnionStmt) lastSelect := union.SelectList.Selects[len(union.SelectList.Selects)-1] endOffset := parser.endOffset(&yyS[yypt-6]) parser.setLastSelectFieldText(lastSelect, endOffset) st := $5.(*ast.SelectStmt) st.IsInBraces = true st.IsAfterUnionDistinct = $3.(bool) endOffset = parser.endOffset(&yyS[yypt-2]) parser.setLastSelectFieldText(st, endOffset) union.SelectList.Selects = append(union.SelectList.Selects, st) if $7 != nil { union.OrderBy = $7.(*ast.OrderByClause) } if $8 != nil { union.Limit = $8.(*ast.Limit) } $$ = union } UnionClauseList: UnionSelect { selectList := &ast.UnionSelectList{Selects: []*ast.SelectStmt{$1.(*ast.SelectStmt)}} $$ = &ast.UnionStmt{ SelectList: selectList, } } | UnionClauseList "UNION" UnionOpt UnionSelect { union := $1.(*ast.UnionStmt) st := $4.(*ast.SelectStmt) st.IsAfterUnionDistinct = $3.(bool) lastSelect := union.SelectList.Selects[len(union.SelectList.Selects)-1] endOffset := parser.endOffset(&yyS[yypt-2]) parser.setLastSelectFieldText(lastSelect, endOffset) union.SelectList.Selects = append(union.SelectList.Selects, st) $$ = union } UnionSelect: SelectStmt { $$ = $1.(interface{}) } | '(' SelectStmt ')' { st := $2.(*ast.SelectStmt) st.IsInBraces = true endOffset := parser.endOffset(&yyS[yypt]) parser.setLastSelectFieldText(st, endOffset) $$ = $2 } UnionOpt: DefaultTrueDistinctOpt /********************Change Statement*******************************/ ChangeStmt: "CHANGE" "PUMP" "TO" "NODE_STATE" eq stringLit forKwd "NODE_ID" stringLit { $$ = &ast.ChangeStmt{ NodeType: ast.PumpType, State: $6, NodeID: $9, } } | "CHANGE" "DRAINER" "TO" "NODE_STATE" eq stringLit forKwd "NODE_ID" stringLit { $$ = &ast.ChangeStmt{ NodeType: ast.DrainerType, State: $6, NodeID: $9, } } /********************Set Statement*******************************/ SetStmt: "SET" VariableAssignmentList { $$ = &ast.SetStmt{Variables: $2.([]*ast.VariableAssignment)} } | "SET" "PASSWORD" eq PasswordOpt { $$ = &ast.SetPwdStmt{Password: $4.(string)} } | "SET" "PASSWORD" "FOR" Username eq PasswordOpt { $$ = &ast.SetPwdStmt{User: $4.(*auth.UserIdentity), Password: $6.(string)} } | "SET" "GLOBAL" "TRANSACTION" TransactionChars { vars := $4.([]*ast.VariableAssignment) for _, v := range vars { v.IsGlobal = true } $$ = &ast.SetStmt{Variables: vars} } | "SET" "SESSION" "TRANSACTION" TransactionChars { $$ = &ast.SetStmt{Variables: $4.([]*ast.VariableAssignment)} } | "SET" "TRANSACTION" TransactionChars { assigns := $3.([]*ast.VariableAssignment) for i:=0; i mysql.MaxFloatPrecisionLength { x.Tp = mysql.TypeDouble } x.Flen = types.UnspecifiedLength } x.Decimal = fopt.Decimal for _, o := range $3.([]*ast.TypeOpt) { if o.IsUnsigned { x.Flag |= mysql.UnsignedFlag } if o.IsZerofill { x.Flag |= mysql.ZerofillFlag } } $$ = x } | BitValueType OptFieldLen { x := types.NewFieldType($1.(byte)) x.Flen = $2.(int) if x.Flen == types.UnspecifiedLength { x.Flen = 1 } $$ = x } IntegerType: "TINYINT" { $$ = mysql.TypeTiny } | "SMALLINT" { $$ = mysql.TypeShort } | "MEDIUMINT" { $$ = mysql.TypeInt24 } | "INT" { $$ = mysql.TypeLong } | "INT1" { $$ = mysql.TypeTiny } | "INT2" { $$ = mysql.TypeShort } | "INT3" { $$ = mysql.TypeInt24 } | "INT4" { $$ = mysql.TypeLong } | "INT8" { $$ = mysql.TypeLonglong } | "INTEGER" { $$ = mysql.TypeLong } | "BIGINT" { $$ = mysql.TypeLonglong } BooleanType: "BOOL" { $$ = mysql.TypeTiny } | "BOOLEAN" { $$ = mysql.TypeTiny } OptInteger: {} | "INTEGER" | "INT" FixedPointType: "DECIMAL" { $$ = mysql.TypeNewDecimal } | "NUMERIC" { $$ = mysql.TypeNewDecimal } | "FIXED" { $$ = mysql.TypeNewDecimal } FloatingPointType: "FLOAT" { $$ = mysql.TypeFloat } | "REAL" { if parser.lexer.GetSQLMode().HasRealAsFloatMode() { $$ = mysql.TypeFloat } else { $$ = mysql.TypeDouble } } | "DOUBLE" { $$ = mysql.TypeDouble } | "DOUBLE" "PRECISION" { $$ = mysql.TypeDouble } BitValueType: "BIT" { $$ = mysql.TypeBit } StringType: Char FieldLen OptBinary { x := types.NewFieldType(mysql.TypeString) x.Flen = $2.(int) x.Charset = $3.(*ast.OptBinary).Charset if $3.(*ast.OptBinary).IsBinary { x.Flag |= mysql.BinaryFlag } $$ = x } | Char OptBinary { x := types.NewFieldType(mysql.TypeString) x.Charset = $2.(*ast.OptBinary).Charset if $2.(*ast.OptBinary).IsBinary { x.Flag |= mysql.BinaryFlag } $$ = x } | NChar FieldLen OptBinary { x := types.NewFieldType(mysql.TypeString) x.Flen = $2.(int) x.Charset = $3.(*ast.OptBinary).Charset if $3.(*ast.OptBinary).IsBinary { x.Flag |= mysql.BinaryFlag } $$ = x } | NChar OptBinary { x := types.NewFieldType(mysql.TypeString) x.Charset = $2.(*ast.OptBinary).Charset if $2.(*ast.OptBinary).IsBinary { x.Flag |= mysql.BinaryFlag } $$ = x } | Varchar FieldLen OptBinary { x := types.NewFieldType(mysql.TypeVarchar) x.Flen = $2.(int) x.Charset = $3.(*ast.OptBinary).Charset if $3.(*ast.OptBinary).IsBinary { x.Flag |= mysql.BinaryFlag } $$ = x } | NVarchar FieldLen OptBinary { x := types.NewFieldType(mysql.TypeVarchar) x.Flen = $2.(int) x.Charset = $3.(*ast.OptBinary).Charset if $3.(*ast.OptBinary).IsBinary { x.Flag |= mysql.BinaryFlag } $$ = x } | "BINARY" OptFieldLen { x := types.NewFieldType(mysql.TypeString) x.Flen = $2.(int) x.Charset = charset.CharsetBin x.Collate = charset.CharsetBin x.Flag |= mysql.BinaryFlag $$ = x } | "VARBINARY" FieldLen { x := types.NewFieldType(mysql.TypeVarchar) x.Flen = $2.(int) x.Charset = charset.CharsetBin x.Collate = charset.CharsetBin x.Flag |= mysql.BinaryFlag $$ = x } | BlobType { x := $1.(*types.FieldType) x.Charset = charset.CharsetBin x.Collate = charset.CharsetBin x.Flag |= mysql.BinaryFlag $$ = $1.(*types.FieldType) } | TextType OptCharsetWithOptBinary { x := $1.(*types.FieldType) x.Charset = $2.(*ast.OptBinary).Charset if $2.(*ast.OptBinary).IsBinary { x.Flag |= mysql.BinaryFlag } $$ = x } | "ENUM" '(' StringList ')' OptCharset { x := types.NewFieldType(mysql.TypeEnum) x.Elems = $3.([]string) x.Charset = $5.(string) $$ = x } | "SET" '(' StringList ')' OptCharset { x := types.NewFieldType(mysql.TypeSet) x.Elems = $3.([]string) x.Charset = $5.(string) $$ = x } | "JSON" { x := types.NewFieldType(mysql.TypeJSON) x.Decimal = 0 x.Charset = charset.CharsetBin x.Collate = charset.CollationBin $$ = x } | "LONG" Varchar OptCharsetWithOptBinary { x := types.NewFieldType(mysql.TypeMediumBlob) x.Charset = $3.(*ast.OptBinary).Charset if $3.(*ast.OptBinary).IsBinary { x.Flag |= mysql.BinaryFlag } $$ = x } | "LONG" OptCharsetWithOptBinary { x := types.NewFieldType(mysql.TypeMediumBlob) x.Charset = $2.(*ast.OptBinary).Charset if $2.(*ast.OptBinary).IsBinary { x.Flag |= mysql.BinaryFlag } $$ = x } Char: "CHARACTER" | "CHAR" NChar: "NCHAR" | "NATIONAL" "CHARACTER" | "NATIONAL" "CHAR" Varchar: "CHARACTER" "VARYING" | "CHAR" "VARYING" | "VARCHAR" | "VARCHARACTER" NVarchar: "NATIONAL" "VARCHAR" | "NATIONAL" "VARCHARACTER" | "NVARCHAR" | "NCHAR" "VARCHAR" | "NCHAR" "VARCHARACTER" | "NATIONAL" "CHARACTER" "VARYING" | "NATIONAL" "CHAR" "VARYING" | "NCHAR" "VARYING" Year: "YEAR" | "SQL_TSI_YEAR" BlobType: "TINYBLOB" { x := types.NewFieldType(mysql.TypeTinyBlob) $$ = x } | "BLOB" OptFieldLen { x := types.NewFieldType(mysql.TypeBlob) x.Flen = $2.(int) $$ = x } | "MEDIUMBLOB" { x := types.NewFieldType(mysql.TypeMediumBlob) $$ = x } | "LONGBLOB" { x := types.NewFieldType(mysql.TypeLongBlob) $$ = x } | "LONG" "VARBINARY" { x := types.NewFieldType(mysql.TypeMediumBlob) $$ = x } TextType: "TINYTEXT" { x := types.NewFieldType(mysql.TypeTinyBlob) $$ = x } | "TEXT" OptFieldLen { x := types.NewFieldType(mysql.TypeBlob) x.Flen = $2.(int) $$ = x } | "MEDIUMTEXT" { x := types.NewFieldType(mysql.TypeMediumBlob) $$ = x } | "LONGTEXT" { x := types.NewFieldType(mysql.TypeLongBlob) $$ = x } OptCharsetWithOptBinary: OptBinary { $$ = $1 } | "ASCII" { $$ = &ast.OptBinary{ IsBinary: false, Charset: charset.CharsetLatin1, } } | "UNICODE" { name, _, err := charset.GetCharsetInfo("ucs2") if err != nil { yylex.AppendError(ErrUnknownCharacterSet.GenWithStackByArgs("ucs2")) return 1 } $$ = &ast.OptBinary{ IsBinary: false, Charset: name, } } | "BYTE" { $$ = &ast.OptBinary{ IsBinary: false, Charset: "", } } DateAndTimeType: "DATE" { x := types.NewFieldType(mysql.TypeDate) $$ = x } | "DATETIME" OptFieldLen { x := types.NewFieldType(mysql.TypeDatetime) x.Flen = mysql.MaxDatetimeWidthNoFsp x.Decimal = $2.(int) if x.Decimal > 0 { x.Flen = x.Flen + 1 + x.Decimal } $$ = x } | "TIMESTAMP" OptFieldLen { x := types.NewFieldType(mysql.TypeTimestamp) x.Flen = mysql.MaxDatetimeWidthNoFsp x.Decimal = $2.(int) if x.Decimal > 0 { x.Flen = x.Flen + 1 + x.Decimal } $$ = x } | "TIME" OptFieldLen { x := types.NewFieldType(mysql.TypeDuration) x.Flen = mysql.MaxDurationWidthNoFsp x.Decimal = $2.(int) if x.Decimal > 0 { x.Flen = x.Flen + 1 + x.Decimal } $$ = x } | Year OptFieldLen FieldOpts { x := types.NewFieldType(mysql.TypeYear) x.Flen = $2.(int) if x.Flen != types.UnspecifiedLength && x.Flen != 4 { yylex.AppendError(ErrInvalidYearColumnLength.GenWithStackByArgs()) return -1 } $$ = x } FieldLen: '(' LengthNum ')' { $$ = int($2.(uint64)) } OptFieldLen: { $$ = types.UnspecifiedLength } | FieldLen { $$ = $1.(int) } FieldOpt: "UNSIGNED" { $$ = &ast.TypeOpt{IsUnsigned: true} } | "SIGNED" { $$ = &ast.TypeOpt{IsUnsigned: false} } | "ZEROFILL" { $$ = &ast.TypeOpt{IsZerofill: true, IsUnsigned: true} } FieldOpts: { $$ = []*ast.TypeOpt{} } | FieldOpts FieldOpt { $$ = append($1.([]*ast.TypeOpt), $2.(*ast.TypeOpt)) } FloatOpt: { $$ = &ast.FloatOpt{Flen: types.UnspecifiedLength, Decimal: types.UnspecifiedLength} } | FieldLen { $$ = &ast.FloatOpt{Flen: $1.(int), Decimal: types.UnspecifiedLength} } | Precision { $$ = $1.(*ast.FloatOpt) } Precision: '(' LengthNum ',' LengthNum ')' { $$ = &ast.FloatOpt{Flen: int($2.(uint64)), Decimal: int($4.(uint64))} } OptBinMod: { $$ = false } | "BINARY" { $$ = true } OptBinary: { $$ = &ast.OptBinary{ IsBinary: false, Charset: "", } } | "BINARY" OptCharset { $$ = &ast.OptBinary{ IsBinary: true, Charset: $2.(string), } } | CharsetKw CharsetName OptBinMod { $$ = &ast.OptBinary{ IsBinary: $3.(bool), Charset: $2.(string), } } OptCharset: { $$ = "" } | CharsetKw CharsetName { $$ = $2.(string) } CharsetKw: "CHARACTER" "SET" | "CHARSET" | "CHAR" "SET" OptCollate: { $$ = "" } | "COLLATE" CollationName { $$ = $2.(string) } StringList: stringLit { $$ = []string{$1} } | StringList ',' stringLit { $$ = append($1.([]string), $3) } StringName: stringLit { $$ = $1 } | Identifier { $$ = $1 } /*********************************************************************************** * Update Statement * See https://dev.mysql.com/doc/refman/5.7/en/update.html ***********************************************************************************/ UpdateStmt: "UPDATE" TableOptimizerHints PriorityOpt IgnoreOptional TableRef "SET" AssignmentList WhereClauseOptional OrderByOptional LimitClause { var refs *ast.Join if x, ok := $5.(*ast.Join); ok { refs = x } else { refs = &ast.Join{Left: $5.(ast.ResultSetNode)} } st := &ast.UpdateStmt{ Priority: $3.(mysql.PriorityEnum), TableRefs: &ast.TableRefsClause{TableRefs: refs}, List: $7.([]*ast.Assignment), IgnoreErr: $4.(bool), } if $2 != nil { st.TableHints = $2.([]*ast.TableOptimizerHint) } if $8 != nil { st.Where = $8.(ast.ExprNode) } if $9 != nil { st.Order = $9.(*ast.OrderByClause) } if $10 != nil { st.Limit = $10.(*ast.Limit) } $$ = st } | "UPDATE" TableOptimizerHints PriorityOpt IgnoreOptional TableRefs "SET" AssignmentList WhereClauseOptional { st := &ast.UpdateStmt{ Priority: $3.(mysql.PriorityEnum), TableRefs: &ast.TableRefsClause{TableRefs: $5.(*ast.Join)}, List: $7.([]*ast.Assignment), IgnoreErr: $4.(bool), } if $2 != nil { st.TableHints = $2.([]*ast.TableOptimizerHint) } if $8 != nil { st.Where = $8.(ast.ExprNode) } $$ = st } UseStmt: "USE" DBName { $$ = &ast.UseStmt{DBName: $2.(string)} } WhereClause: "WHERE" Expression { $$ = $2 } WhereClauseOptional: { $$ = nil } | WhereClause { $$ = $1 } CommaOpt: {} | ',' {} /************************************************************************************ * Account Management Statements * https://dev.mysql.com/doc/refman/5.7/en/account-management-sql.html ************************************************************************************/ CreateUserStmt: "CREATE" "USER" IfNotExists UserSpecList RequireClauseOpt ConnectionOptions PasswordOrLockOptions { // See https://dev.mysql.com/doc/refman/5.7/en/create-user.html $$ = &ast.CreateUserStmt{ IsCreateRole: false, IfNotExists: $3.(bool), Specs: $4.([]*ast.UserSpec), TslOptions: $5.([]*ast.TslOption), ResourceOptions: $6.([]*ast.ResourceOption), PasswordOrLockOptions: $7.([]*ast.PasswordOrLockOption), } } CreateRoleStmt: "CREATE" "ROLE" IfNotExists RoleSpecList { // See https://dev.mysql.com/doc/refman/8.0/en/create-role.html $$ = &ast.CreateUserStmt{ IsCreateRole: true, IfNotExists: $3.(bool), Specs: $4.([]*ast.UserSpec), } } /* See http://dev.mysql.com/doc/refman/5.7/en/alter-user.html */ AlterUserStmt: "ALTER" "USER" IfExists UserSpecList RequireClauseOpt ConnectionOptions PasswordOrLockOptions { $$ = &ast.AlterUserStmt{ IfExists: $3.(bool), Specs: $4.([]*ast.UserSpec), TslOptions: $5.([]*ast.TslOption), ResourceOptions: $6.([]*ast.ResourceOption), PasswordOrLockOptions: $7.([]*ast.PasswordOrLockOption), } } | "ALTER" "USER" IfExists "USER" '(' ')' "IDENTIFIED" "BY" AuthString { auth := &ast.AuthOption { AuthString: $9.(string), ByAuthString: true, } $$ = &ast.AlterUserStmt{ IfExists: $3.(bool), CurrentAuth: auth, } } UserSpec: Username AuthOption { userSpec := &ast.UserSpec{ User: $1.(*auth.UserIdentity), } if $2 != nil { userSpec.AuthOpt = $2.(*ast.AuthOption) } $$ = userSpec } UserSpecList: UserSpec { $$ = []*ast.UserSpec{$1.(*ast.UserSpec)} } | UserSpecList ',' UserSpec { $$ = append($1.([]*ast.UserSpec), $3.(*ast.UserSpec)) } ConnectionOptions: { l := []*ast.ResourceOption{} $$ = l } | "WITH" ConnectionOptionList { $$ = $2 yylex.AppendError(yylex.Errorf("TiDB does not support WITH ConnectionOptions now, they would be parsed but ignored.")) parser.lastErrorAsWarn() } ConnectionOptionList: ConnectionOption { $$ = []*ast.ResourceOption{$1.(*ast.ResourceOption)} } | ConnectionOptionList ConnectionOption { l := $1.([]*ast.ResourceOption) l = append(l, $2.(*ast.ResourceOption)) $$ = l } ConnectionOption: "MAX_QUERIES_PER_HOUR" NUM { $$ = &ast.ResourceOption { Type: ast.MaxQueriesPerHour, Count: $2.(int64), } } | "MAX_UPDATES_PER_HOUR" NUM { $$ = &ast.ResourceOption { Type: ast.MaxUpdatesPerHour, Count: $2.(int64), } } | "MAX_CONNECTIONS_PER_HOUR" NUM { $$ = &ast.ResourceOption { Type: ast.MaxConnectionsPerHour, Count: $2.(int64), } } | "MAX_USER_CONNECTIONS" NUM { $$ = &ast.ResourceOption { Type: ast.MaxUserConnections, Count: $2.(int64), } } RequireClauseOpt: { $$ = []*ast.TslOption{} } | RequireClause { $$ = $1 yylex.AppendError(yylex.Errorf("TiDB does not support REQUIRE now, they would be parsed but ignored.")) parser.lastErrorAsWarn() } RequireClause: "REQUIRE" "NONE" { t := &ast.TslOption { Type: ast.TslNone, } $$ = []*ast.TslOption{t} } | "REQUIRE" "SSL" { t := &ast.TslOption { Type: ast.Ssl, } $$ = []*ast.TslOption{t} } | "REQUIRE" "X509" { t := &ast.TslOption { Type: ast.X509, } $$ = []*ast.TslOption{t} } | "REQUIRE" RequireList { $$ = $2 } RequireList: RequireListElement { $$ = []*ast.TslOption{$1.(*ast.TslOption)} } | RequireListElement "AND" RequireList { l := $3.([]*ast.TslOption) l = append(l, $1.(*ast.TslOption)) $$ = l } RequireListElement: "ISSUER" stringLit { $$ = &ast.TslOption { Type: ast.Issuer, Value: $2, } } | "SUBJECT" stringLit { $$ = &ast.TslOption { Type: ast.Subject, Value: $2, } } | "CIPHER" stringLit { $$ = &ast.TslOption { Type: ast.Cipher, Value: $2, } } PasswordOrLockOptions: { l := []*ast.PasswordOrLockOption{} $$ = l } | PasswordOrLockOptionList { $$ = $1 yylex.AppendError(yylex.Errorf("TiDB does not support PASSWORD EXPIRE and ACCOUNT LOCK now, they would be parsed but ignored.")) parser.lastErrorAsWarn() } PasswordOrLockOptionList: PasswordOrLockOption { $$ = []*ast.PasswordOrLockOption{$1.(*ast.PasswordOrLockOption)} } | PasswordOrLockOptionList PasswordOrLockOption { l := $1.([]*ast.PasswordOrLockOption) l = append(l, $2.(*ast.PasswordOrLockOption)) $$ = l } PasswordOrLockOption: "ACCOUNT" "UNLOCK" { $$ = &ast.PasswordOrLockOption { Type: ast.Unlock, } } | "ACCOUNT" "LOCK" { $$ = &ast.PasswordOrLockOption { Type: ast.Lock, } } | PasswordExpire { $$ = &ast.PasswordOrLockOption { Type: ast.PasswordExpire, } } | PasswordExpire "INTERVAL" NUM "DAY" { $$ = &ast.PasswordOrLockOption { Type: ast.PasswordExpireInterval, Count: $3.(int64), } } | PasswordExpire "NEVER" { $$ = &ast.PasswordOrLockOption { Type: ast.PasswordExpireNever, } } | PasswordExpire "DEFAULT" { $$ = &ast.PasswordOrLockOption { Type: ast.PasswordExpireDefault, } } PasswordExpire: "PASSWORD" "EXPIRE" ClearPasswordExpireOptions { $$ = nil } ClearPasswordExpireOptions: { $$ = nil } AuthOption: { $$ = nil } | "IDENTIFIED" "BY" AuthString { $$ = &ast.AuthOption { AuthString: $3.(string), ByAuthString: true, } } | "IDENTIFIED" "WITH" StringName { $$ = nil } | "IDENTIFIED" "WITH" StringName "BY" AuthString { $$ = &ast.AuthOption { AuthString: $5.(string), ByAuthString: true, } } | "IDENTIFIED" "WITH" StringName "AS" HashString { $$ = &ast.AuthOption{ HashString: $5.(string), } } | "IDENTIFIED" "BY" "PASSWORD" HashString { $$ = &ast.AuthOption{ HashString: $4.(string), } } HashString: stringLit { $$ = $1 } RoleSpec: Rolename { role := $1.(*auth.RoleIdentity) roleSpec := &ast.UserSpec{ User: &auth.UserIdentity { Username: role.Username, Hostname: role.Hostname, }, IsRole: true, } $$ = roleSpec } RoleSpecList: RoleSpec { $$ = []*ast.UserSpec{$1.(*ast.UserSpec)} } | RoleSpecList ',' RoleSpec { $$ = append($1.([]*ast.UserSpec), $3.(*ast.UserSpec)) } /******************************************************************* * * Create Binding Statement * * Example: * CREATE GLOBAL BINDING FOR select Col1,Col2 from table USING select Col1,Col2 from table use index(Col1) *******************************************************************/ CreateBindingStmt: "CREATE" GlobalScope "BINDING" "FOR" SelectStmt "USING" SelectStmt { startOffset := parser.startOffset(&yyS[yypt-2]) endOffset := parser.startOffset(&yyS[yypt-1]) selStmt := $5.(*ast.SelectStmt) selStmt.SetText(strings.TrimSpace(parser.src[startOffset:endOffset])) startOffset = parser.startOffset(&yyS[yypt]) hintedSelStmt := $7.(*ast.SelectStmt) hintedSelStmt.SetText(strings.TrimSpace(parser.src[startOffset:])) x := &ast.CreateBindingStmt { OriginSel: selStmt, HintedSel: hintedSelStmt, GlobalScope: $2.(bool), } $$ = x } /******************************************************************* * * Drop Binding Statement * * Example: * DROP GLOBAL BINDING FOR select Col1,Col2 from table *******************************************************************/ DropBindingStmt: "DROP" GlobalScope "BINDING" "FOR" SelectStmt { startOffset := parser.startOffset(&yyS[yypt]) selStmt := $5.(*ast.SelectStmt) selStmt.SetText(strings.TrimSpace(parser.src[startOffset:])) x := &ast.DropBindingStmt { OriginSel: selStmt, GlobalScope: $2.(bool), } $$ = x } /************************************************************************************* * Grant statement * See https://dev.mysql.com/doc/refman/5.7/en/grant.html *************************************************************************************/ GrantStmt: "GRANT" PrivElemList "ON" ObjectType PrivLevel "TO" UserSpecList WithGrantOptionOpt { $$ = &ast.GrantStmt{ Privs: $2.([]*ast.PrivElem), ObjectType: $4.(ast.ObjectTypeType), Level: $5.(*ast.GrantLevel), Users: $7.([]*ast.UserSpec), WithGrant: $8.(bool), } } GrantRoleStmt: "GRANT" RolenameList "TO" UsernameList { $$ = &ast.GrantRoleStmt { Roles: $2.([]*auth.RoleIdentity), Users: $4.([]*auth.UserIdentity), } } WithGrantOptionOpt: { $$ = false } | "WITH" "GRANT" "OPTION" { $$ = true } | "WITH" "MAX_QUERIES_PER_HOUR" NUM { $$ = false } | "WITH" "MAX_UPDATES_PER_HOUR" NUM { $$ = false } | "WITH" "MAX_CONNECTIONS_PER_HOUR" NUM { $$ = false } | "WITH" "MAX_USER_CONNECTIONS" NUM { $$ = false } PrivElem: PrivType { $$ = &ast.PrivElem{ Priv: $1.(mysql.PrivilegeType), } } | PrivType '(' ColumnNameList ')' { $$ = &ast.PrivElem{ Priv: $1.(mysql.PrivilegeType), Cols: $3.([]*ast.ColumnName), } } PrivElemList: PrivElem { $$ = []*ast.PrivElem{$1.(*ast.PrivElem)} } | PrivElemList ',' PrivElem { $$ = append($1.([]*ast.PrivElem), $3.(*ast.PrivElem)) } PrivType: "ALL" { $$ = mysql.AllPriv } | "ALL" "PRIVILEGES" { $$ = mysql.AllPriv } | "ALTER" { $$ = mysql.AlterPriv } | "CREATE" { $$ = mysql.CreatePriv } | "CREATE" "USER" { $$ = mysql.CreateUserPriv } | "TRIGGER" { $$ = mysql.TriggerPriv } | "DELETE" { $$ = mysql.DeletePriv } | "DROP" { $$ = mysql.DropPriv } | "PROCESS" { $$ = mysql.ProcessPriv } | "EXECUTE" { $$ = mysql.ExecutePriv } | "INDEX" { $$ = mysql.IndexPriv } | "INSERT" { $$ = mysql.InsertPriv } | "SELECT" { $$ = mysql.SelectPriv } | "SUPER" { $$ = mysql.SuperPriv } | "SHOW" "DATABASES" { $$ = mysql.ShowDBPriv } | "UPDATE" { $$ = mysql.UpdatePriv } | "GRANT" "OPTION" { $$ = mysql.GrantPriv } | "REFERENCES" { $$ = mysql.ReferencesPriv } | "REPLICATION" "SLAVE" { $$ = mysql.PrivilegeType(0) } | "REPLICATION" "CLIENT" { $$ = mysql.PrivilegeType(0) } | "USAGE" { $$ = mysql.PrivilegeType(0) } | "RELOAD" { $$ = mysql.PrivilegeType(0) } | "CREATE" "TEMPORARY" "TABLES" { $$ = mysql.CreateTMPTablePriv } | "LOCK" "TABLES" { $$ = mysql.LockTablesPriv } | "CREATE" "VIEW" { $$ = mysql.CreateViewPriv } | "SHOW" "VIEW" { $$ = mysql.ShowViewPriv } | "CREATE" "ROLE" { $$ = mysql.CreateRolePriv } | "DROP" "ROLE" { $$ = mysql.DropRolePriv } | "CREATE" "ROUTINE" { $$ = mysql.CreateRoutinePriv } | "ALTER" "ROUTINE" { $$ = mysql.AlterRoutinePriv } | "EVENT" { $$ = mysql.EventPriv } | "SHUTDOWN" { $$ = mysql.ShutdownPriv } ObjectType: { $$ = ast.ObjectTypeNone } | "TABLE" { $$ = ast.ObjectTypeTable } PrivLevel: '*' { $$ = &ast.GrantLevel { Level: ast.GrantLevelDB, } } | '*' '.' '*' { $$ = &ast.GrantLevel { Level: ast.GrantLevelGlobal, } } | Identifier '.' '*' { $$ = &ast.GrantLevel { Level: ast.GrantLevelDB, DBName: $1, } } | Identifier '.' Identifier { $$ = &ast.GrantLevel { Level: ast.GrantLevelTable, DBName: $1, TableName: $3, } } | Identifier { $$ = &ast.GrantLevel { Level: ast.GrantLevelTable, TableName: $1, } } /**************************************RevokeStmt******************************************* * See https://dev.mysql.com/doc/refman/5.7/en/revoke.html *******************************************************************************************/ RevokeStmt: "REVOKE" PrivElemList "ON" ObjectType PrivLevel "FROM" UserSpecList { $$ = &ast.RevokeStmt{ Privs: $2.([]*ast.PrivElem), ObjectType: $4.(ast.ObjectTypeType), Level: $5.(*ast.GrantLevel), Users: $7.([]*ast.UserSpec), } } RevokeRoleStmt: "REVOKE" RolenameList "FROM" UsernameList { $$ = &ast.RevokeRoleStmt { Roles: $2.([]*auth.RoleIdentity), Users: $4.([]*auth.UserIdentity), } } /**************************************LoadDataStmt***************************************** * See https://dev.mysql.com/doc/refman/5.7/en/load-data.html *******************************************************************************************/ LoadDataStmt: "LOAD" "DATA" LocalOpt "INFILE" stringLit DuplicateOpt "INTO" "TABLE" TableName CharsetOpt Fields Lines IgnoreLines ColumnNameOrUserVarListOptWithBrackets LoadDataSetSpecOpt { x := &ast.LoadDataStmt{ Path: $5, OnDuplicate: $6.(ast.OnDuplicateKeyHandlingType), Table: $9.(*ast.TableName), ColumnsAndUserVars: $14.([]*ast.ColumnNameOrUserVar), IgnoreLines: $13.(uint64), } if $3 != nil { x.IsLocal = true // See https://dev.mysql.com/doc/refman/5.7/en/load-data.html#load-data-duplicate-key-handling // If you do not specify IGNORE or REPLACE modifier , then we set default behavior to IGNORE when LOCAL modifier is specified if x.OnDuplicate == ast.OnDuplicateKeyHandlingError { x.OnDuplicate = ast.OnDuplicateKeyHandlingIgnore } } if $11 != nil { x.FieldsInfo = $11.(*ast.FieldsClause) } if $12 != nil { x.LinesInfo = $12.(*ast.LinesClause) } if $15 != nil { x.ColumnAssignments = $15.([]*ast.Assignment) } columns := []*ast.ColumnName{} for _, v := range x.ColumnsAndUserVars { if v.ColumnName != nil { columns = append(columns, v.ColumnName) } } x.Columns = columns $$ = x } IgnoreLines: { $$ = uint64(0) } | "IGNORE" NUM "LINES" { $$ = getUint64FromNUM($2) } CharsetOpt: {} | "CHARACTER" "SET" CharsetName LocalOpt: { $$ = nil } | "LOCAL" { $$ = $1 } Fields: { escape := "\\" $$ = &ast.FieldsClause{ Terminated: "\t", Escaped: escape[0], } } | FieldsOrColumns FieldItemList { fieldsClause := &ast.FieldsClause{ Terminated: "\t", Escaped: []byte("\\")[0], } fieldItems := $2.([]*ast.FieldItem) for _, item := range fieldItems { switch item.Type { case ast.Terminated: fieldsClause.Terminated = item.Value case ast.Enclosed: var enclosed byte if len(item.Value) > 0 { enclosed = item.Value[0] } fieldsClause.Enclosed = enclosed case ast.Escaped: var escaped byte if len(item.Value) > 0 { escaped = item.Value[0] } fieldsClause.Escaped = escaped } } $$ = fieldsClause } FieldsOrColumns: "FIELDS" | "COLUMNS" FieldItemList: FieldItemList FieldItem { fieldItems := $1.([]*ast.FieldItem) $$ = append(fieldItems, $2.(*ast.FieldItem)) } | FieldItem { fieldItems := make([]*ast.FieldItem, 1, 1) fieldItems[0] = $1.(*ast.FieldItem) $$ = fieldItems } FieldItem: "TERMINATED" "BY" FieldTerminator { $$ = &ast.FieldItem{ Type: ast.Terminated, Value: $3.(string), } } | "OPTIONALLY" "ENCLOSED" "BY" FieldTerminator { str := $4.(string) if str != "\\" && len(str) > 1 { yylex.AppendError(ErrWrongFieldTerminators.GenWithStackByArgs()) return 1 } $$ = &ast.FieldItem{ Type: ast.Enclosed, Value: str, } } | "ENCLOSED" "BY" FieldTerminator { str := $3.(string) if str != "\\" && len(str) > 1 { yylex.AppendError(ErrWrongFieldTerminators.GenWithStackByArgs()) return 1 } $$ = &ast.FieldItem{ Type: ast.Enclosed, Value: str, } } | "ESCAPED" "BY" FieldTerminator { str := $3.(string) if str != "\\" && len(str) > 1 { yylex.AppendError(ErrWrongFieldTerminators.GenWithStackByArgs()) return 1 } $$ = &ast.FieldItem{ Type: ast.Escaped, Value: str, } } FieldTerminator: stringLit { $$ = $1 } | hexLit { $$ = $1.(ast.BinaryLiteral).ToString() } | bitLit { $$ = $1.(ast.BinaryLiteral).ToString() } Lines: { $$ = &ast.LinesClause{Terminated: "\n"} } | "LINES" Starting LinesTerminated { $$ = &ast.LinesClause{Starting: $2.(string), Terminated: $3.(string)} } Starting: { $$ = "" } | "STARTING" "BY" stringLit { $$ = $3 } LinesTerminated: { $$ = "\n" } | "TERMINATED" "BY" stringLit { $$ = $3 } LoadDataSetSpecOpt: { $$ = nil } | "SET" LoadDataSetList { $$ = $2 } LoadDataSetList: LoadDataSetList ',' LoadDataSetItem { l := $1.([]*ast.Assignment) $$ = append(l, $3.(*ast.Assignment)) } | LoadDataSetItem { $$ = []*ast.Assignment{$1.(*ast.Assignment)} } LoadDataSetItem: SimpleIdent "=" ExprOrDefault { $$ = &ast.Assignment{ Column: $1.(*ast.ColumnNameExpr).Name, Expr: $3, } } /********************************************************************* * Lock/Unlock Tables * See http://dev.mysql.com/doc/refman/5.7/en/lock-tables.html * All the statement leaves empty. This is used to prevent mysqldump error. *********************************************************************/ UnlockTablesStmt: "UNLOCK" TablesTerminalSym { $$ = &ast.UnlockTablesStmt{} } LockTablesStmt: "LOCK" TablesTerminalSym TableLockList { $$ = &ast.LockTablesStmt{ TableLocks: $3.([]ast.TableLock), } } TablesTerminalSym: "TABLES" | "TABLE" TableLock: TableName LockType { $$ = ast.TableLock{ Table: $1.(*ast.TableName), Type: $2.(model.TableLockType), } } LockType: "READ" { $$ = model.TableLockRead } | "READ" "LOCAL" { $$ = model.TableLockReadLocal } | "WRITE" { $$ = model.TableLockWrite } | "WRITE" "LOCAL" { $$ = model.TableLockWriteLocal } TableLockList: TableLock { $$ = []ast.TableLock{$1.(ast.TableLock)} } | TableLockList ',' TableLock { $$ = append($1.([]ast.TableLock), $3.(ast.TableLock)) } /******************************************************************** * Kill Statement * See https://dev.mysql.com/doc/refman/5.7/en/kill.html *******************************************************************/ KillStmt: KillOrKillTiDB NUM { $$ = &ast.KillStmt{ ConnectionID: getUint64FromNUM($2), TiDBExtension: $1.(bool), } } | KillOrKillTiDB "CONNECTION" NUM { $$ = &ast.KillStmt{ ConnectionID: getUint64FromNUM($3), TiDBExtension: $1.(bool), } } | KillOrKillTiDB "QUERY" NUM { $$ = &ast.KillStmt{ ConnectionID: getUint64FromNUM($3), Query: true, TiDBExtension: $1.(bool), } } KillOrKillTiDB: "KILL" { $$ = false } /* KILL TIDB is a special grammar extension in TiDB, it can be used only when the client connect to TiDB directly, not proxied under LVS. */ | "KILL" "TIDB" { $$ = true } /*******************************************************************************************/ LoadStatsStmt: "LOAD" "STATS" stringLit { $$ = &ast.LoadStatsStmt{ Path: $3, } } %%