Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4688 lines
96 KiB
4688 lines
96 KiB
%{ |
|
// 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. |
|
|
|
// Inital 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/tidb/mysql" |
|
"github.com/pingcap/tidb/ast" |
|
"github.com/pingcap/tidb/model" |
|
"github.com/pingcap/tidb/parser/opcode" |
|
"github.com/pingcap/tidb/util/charset" |
|
"github.com/pingcap/tidb/util/types" |
|
) |
|
|
|
%} |
|
|
|
%union { |
|
offset int // offset |
|
line int |
|
col int |
|
item interface{} |
|
list []interface{} |
|
} |
|
|
|
%token <item> |
|
|
|
/*yy:token "1.%d" */ floatLit "floating-point literal" |
|
/*yy:token "%c" */ identifier "identifier" |
|
/*yy:token "%d" */ intLit "integer literal" |
|
/*yy:token "\"%c\"" */ stringLit "string literal" |
|
/*yy:token "%x" */ hexLit "hexadecimal literal" |
|
/*yy:token "%b" */ bitLit "bit literal" |
|
|
|
|
|
abs "ABS" |
|
add "ADD" |
|
addDate "ADDDATE" |
|
admin "ADMIN" |
|
after "AFTER" |
|
all "ALL" |
|
alter "ALTER" |
|
and "AND" |
|
andand "&&" |
|
andnot "&^" |
|
any "ANY" |
|
as "AS" |
|
asc "ASC" |
|
at "AT" |
|
autoIncrement "AUTO_INCREMENT" |
|
avg "AVG" |
|
avgRowLength "AVG_ROW_LENGTH" |
|
begin "BEGIN" |
|
between "BETWEEN" |
|
both "BOTH" |
|
btree "BTREE" |
|
by "BY" |
|
byteType "BYTE" |
|
caseKwd "CASE" |
|
cast "CAST" |
|
character "CHARACTER" |
|
charsetKwd "CHARSET" |
|
check "CHECK" |
|
checksum "CHECKSUM" |
|
coalesce "COALESCE" |
|
collate "COLLATE" |
|
collation "COLLATION" |
|
column "COLUMN" |
|
columns "COLUMNS" |
|
comment "COMMENT" |
|
commit "COMMIT" |
|
committed "COMMITTED" |
|
compact "COMPACT" |
|
compressed "COMPRESSED" |
|
compression "COMPRESSION" |
|
concat "CONCAT" |
|
concatWs "CONCAT_WS" |
|
connection "CONNECTION" |
|
connectionID "CONNECTION_ID" |
|
constraint "CONSTRAINT" |
|
convert "CONVERT" |
|
count "COUNT" |
|
create "CREATE" |
|
cross "CROSS" |
|
curDate "CURDATE" |
|
currentDate "CURRENT_DATE" |
|
curTime "CUR_TIME" |
|
currentTime "CURRENT_TIME" |
|
currentUser "CURRENT_USER" |
|
database "DATABASE" |
|
databases "DATABASES" |
|
dateAdd "DATE_ADD" |
|
dateSub "DATE_SUB" |
|
day "DAY" |
|
dayname "DAYNAME" |
|
dayofmonth "DAYOFMONTH" |
|
dayofweek "DAYOFWEEK" |
|
dayofyear "DAYOFYEAR" |
|
ddl "DDL" |
|
deallocate "DEALLOCATE" |
|
defaultKwd "DEFAULT" |
|
delayed "DELAYED" |
|
delayKeyWrite "DELAY_KEY_WRITE" |
|
deleteKwd "DELETE" |
|
desc "DESC" |
|
describe "DESCRIBE" |
|
distinct "DISTINCT" |
|
div "DIV" |
|
do "DO" |
|
drop "DROP" |
|
dual "DUAL" |
|
duplicate "DUPLICATE" |
|
dynamic "DYNAMIC" |
|
elseKwd "ELSE" |
|
end "END" |
|
engine "ENGINE" |
|
engines "ENGINES" |
|
enum "ENUM" |
|
eq "=" |
|
escape "ESCAPE" |
|
execute "EXECUTE" |
|
exists "EXISTS" |
|
explain "EXPLAIN" |
|
extract "EXTRACT" |
|
falseKwd "false" |
|
fields "FIELDS" |
|
first "FIRST" |
|
fixed "FIXED" |
|
foreign "FOREIGN" |
|
forKwd "FOR" |
|
foundRows "FOUND_ROWS" |
|
from "FROM" |
|
full "FULL" |
|
fulltext "FULLTEXT" |
|
ge ">=" |
|
global "GLOBAL" |
|
grant "GRANT" |
|
grants "GRANTS" |
|
group "GROUP" |
|
groupConcat "GROUP_CONCAT" |
|
hash "HASH" |
|
having "HAVING" |
|
highPriority "HIGH_PRIORITY" |
|
hour "HOUR" |
|
identified "IDENTIFIED" |
|
ignore "IGNORE" |
|
ifKwd "IF" |
|
ifNull "IFNULL" |
|
in "IN" |
|
index "INDEX" |
|
inner "INNER" |
|
insert "INSERT" |
|
interval "INTERVAL" |
|
into "INTO" |
|
is "IS" |
|
isolation "ISOLATION" |
|
join "JOIN" |
|
key "KEY" |
|
keyBlockSize "KEY_BLOCK_SIZE" |
|
le "<=" |
|
leading "LEADING" |
|
left "LEFT" |
|
length "LENGTH" |
|
level "LEVEL" |
|
like "LIKE" |
|
limit "LIMIT" |
|
local "LOCAL" |
|
locate "LOCATE" |
|
lock "LOCK" |
|
lower "LOWER" |
|
lowPriority "LOW_PRIORITY" |
|
lsh "<<" |
|
max "MAX" |
|
maxRows "MAX_ROWS" |
|
microsecond "MICROSECOND" |
|
min "MIN" |
|
minute "MINUTE" |
|
minRows "MIN_ROWS" |
|
mod "MOD" |
|
mode "MODE" |
|
month "MONTH" |
|
names "NAMES" |
|
national "NATIONAL" |
|
neq "!=" |
|
neqSynonym "<>" |
|
not "NOT" |
|
null "NULL" |
|
nulleq "<=>" |
|
nullIf "NULLIF" |
|
offset "OFFSET" |
|
on "ON" |
|
only "ONLY" |
|
option "OPTION" |
|
or "OR" |
|
order "ORDER" |
|
oror "||" |
|
outer "OUTER" |
|
password "PASSWORD" |
|
placeholder "PLACEHOLDER" |
|
pow "POW" |
|
power "POWER" |
|
prepare "PREPARE" |
|
primary "PRIMARY" |
|
procedure "PROCEDURE" |
|
quarter "QUARTER" |
|
quick "QUICK" |
|
rand "RAND" |
|
read "READ" |
|
redundant "REDUNDANT" |
|
references "REFERENCES" |
|
regexpKwd "REGEXP" |
|
repeat "REPEAT" |
|
repeatable "REPEATABLE" |
|
replace "REPLACE" |
|
right "RIGHT" |
|
rlike "RLIKE" |
|
rollback "ROLLBACK" |
|
row "ROW" |
|
rowFormat "ROW_FORMAT" |
|
rsh ">>" |
|
schema "SCHEMA" |
|
schemas "SCHEMAS" |
|
second "SECOND" |
|
selectKwd "SELECT" |
|
serializable "SERIALIZABLE" |
|
session "SESSION" |
|
set "SET" |
|
share "SHARE" |
|
show "SHOW" |
|
signed "SIGNED" |
|
some "SOME" |
|
start "START" |
|
status "STATUS" |
|
stringType "string" |
|
subDate "SUBDATE" |
|
strcmp "STRCMP" |
|
substring "SUBSTRING" |
|
substringIndex "SUBSTRING_INDEX" |
|
sum "SUM" |
|
sysVar "SYS_VAR" |
|
sysDate "SYSDATE" |
|
tableKwd "TABLE" |
|
tables "TABLES" |
|
then "THEN" |
|
to "TO" |
|
trailing "TRAILING" |
|
transaction "TRANSACTION" |
|
triggers "TRIGGERS" |
|
trim "TRIM" |
|
trueKwd "true" |
|
truncate "TRUNCATE" |
|
uncommitted "UNCOMMITTED" |
|
underscoreCS "UNDERSCORE_CHARSET" |
|
unknown "UNKNOWN" |
|
union "UNION" |
|
unique "UNIQUE" |
|
unlock "UNLOCK" |
|
unsigned "UNSIGNED" |
|
update "UPDATE" |
|
upper "UPPER" |
|
use "USE" |
|
user "USER" |
|
using "USING" |
|
userVar "USER_VAR" |
|
value "VALUE" |
|
values "VALUES" |
|
variables "VARIABLES" |
|
version "VERSION" |
|
warnings "WARNINGS" |
|
week "WEEK" |
|
weekday "WEEKDAY" |
|
weekofyear "WEEKOFYEAR" |
|
when "WHEN" |
|
where "WHERE" |
|
write "WRITE" |
|
xor "XOR" |
|
yearweek "YEARWEEK" |
|
zerofill "ZEROFILL" |
|
|
|
calcFoundRows "SQL_CALC_FOUND_ROWS" |
|
|
|
currentTs "CURRENT_TIMESTAMP" |
|
localTime "LOCALTIME" |
|
localTs "LOCALTIMESTAMP" |
|
now "NOW" |
|
|
|
tinyIntType "TINYINT" |
|
smallIntType "SMALLINT" |
|
mediumIntType "MEDIUMINT" |
|
intType "INT" |
|
integerType "INTEGER" |
|
bigIntType "BIGINT" |
|
bitType "BIT" |
|
|
|
decimalType "DECIMAL" |
|
numericType "NUMERIC" |
|
floatType "float" |
|
doubleType "DOUBLE" |
|
precisionType "PRECISION" |
|
realType "REAL" |
|
|
|
dateType "DATE" |
|
timeType "TIME" |
|
datetimeType "DATETIME" |
|
timestampType "TIMESTAMP" |
|
yearType "YEAR" |
|
|
|
charType "CHAR" |
|
varcharType "VARCHAR" |
|
binaryType "BINARY" |
|
varbinaryType "VARBINARY" |
|
tinyblobType "TINYBLOB" |
|
blobType "BLOB" |
|
mediumblobType "MEDIUMBLOB" |
|
longblobType "LONGBLOB" |
|
tinytextType "TINYTEXT" |
|
textType "TEXT" |
|
mediumtextType "MEDIUMTEXT" |
|
longtextType "LONGTEXT" |
|
|
|
int16Type "int16" |
|
int24Type "int24" |
|
int32Type "int32" |
|
int64Type "int64" |
|
int8Type "int8" |
|
uintType "uint" |
|
uint16Type "uint16" |
|
uint32Type "uint32" |
|
uint64Type "uint64" |
|
uint8Type "uint8", |
|
float32Type "float32" |
|
float64Type "float64" |
|
boolType "BOOL" |
|
booleanType "BOOLEAN" |
|
|
|
parseExpression "parse expression prefix" |
|
|
|
secondMicrosecond "SECOND_MICROSECOND" |
|
minuteMicrosecond "MINUTE_MICROSECOND" |
|
minuteSecond "MINUTE_SECOND" |
|
hourMicrosecond "HOUR_MICROSECOND" |
|
hourSecond "HOUR_SECOND" |
|
hourMinute "HOUR_MINUTE" |
|
dayMicrosecond "DAY_MICROSECOND" |
|
daySecond "DAY_SECOND" |
|
dayMinute "DAY_MINUTE" |
|
dayHour "DAY_HOUR" |
|
yearMonth "YEAR_MONTH" |
|
|
|
%type <item> |
|
AdminStmt "Check table statement or show ddl statement" |
|
AlterTableStmt "Alter table statement" |
|
AlterTableSpec "Alter table specification" |
|
AlterTableSpecList "Alter table specification list" |
|
AnyOrAll "Any or All for subquery" |
|
Assignment "assignment" |
|
AssignmentList "assignment list" |
|
AssignmentListOpt "assignment list opt" |
|
AuthOption "User auth option" |
|
AuthString "Password string value" |
|
BeginTransactionStmt "BEGIN TRANSACTION statement" |
|
CastType "Cast function target type" |
|
ColumnDef "table column definition" |
|
ColumnName "column name" |
|
ColumnNameList "column name list" |
|
ColumnNameListOpt "column name list opt" |
|
ColumnKeywordOpt "Column keyword or empty" |
|
ColumnSetValue "insert statement set value by column name" |
|
ColumnSetValueList "insert statement set value by column name list" |
|
CommaOpt "optional comma" |
|
CommitStmt "COMMIT statement" |
|
CompareOp "Compare opcode" |
|
ColumnOption "column definition option" |
|
ColumnOptionList "column definition option list" |
|
ColumnOptionListOpt "optional column definition option list" |
|
Constraint "table constraint" |
|
ConstraintElem "table constraint element" |
|
ConstraintKeywordOpt "Constraint Keyword or empty" |
|
CreateDatabaseStmt "Create Database Statement" |
|
CreateIndexStmt "CREATE INDEX statement" |
|
CreateIndexStmtUnique "CREATE INDEX optional UNIQUE clause" |
|
DatabaseOption "CREATE Database specification" |
|
DatabaseOptionList "CREATE Database specification list" |
|
DatabaseOptionListOpt "CREATE Database specification list opt" |
|
CreateTableStmt "CREATE TABLE statement" |
|
CreateUserStmt "CREATE User statement" |
|
CrossOpt "Cross join option" |
|
DateArithOpt "Date arith dateadd or datesub option" |
|
DateArithMultiFormsOpt "Date arith adddate or subdate option" |
|
DateArithInterval "Date arith interval part" |
|
DatabaseSym "DATABASE or SCHEMA" |
|
DBName "Database Name" |
|
DeallocateSym "Deallocate or drop" |
|
DeallocateStmt "Deallocate prepared statement" |
|
Default "DEFAULT clause" |
|
DefaultOpt "optional DEFAULT clause" |
|
DefaultKwdOpt "optional DEFAULT keyword" |
|
DefaultValueExpr "DefaultValueExpr(Now or Signed Literal)" |
|
DeleteFromStmt "DELETE FROM statement" |
|
DistinctOpt "Distinct option" |
|
DoStmt "Do statement" |
|
DropDatabaseStmt "DROP DATABASE statement" |
|
DropIndexStmt "DROP INDEX statement" |
|
DropTableStmt "DROP TABLE statement" |
|
EmptyStmt "empty statement" |
|
EqOpt "= or empty" |
|
EscapedTableRef "escaped table reference" |
|
ExecuteStmt "Execute statement" |
|
ExplainSym "EXPLAIN or DESCRIBE or DESC" |
|
ExplainStmt "EXPLAIN statement" |
|
Expression "expression" |
|
ExpressionList "expression list" |
|
ExpressionListOpt "expression list opt" |
|
ExpressionListList "expression list list" |
|
Factor "expression factor" |
|
PredicateExpr "Predicate expression factor" |
|
Field "field expression" |
|
FieldAsName "Field alias name" |
|
FieldAsNameOpt "Field alias name opt" |
|
FieldList "field expression list" |
|
TableRefsClause "Table references clause" |
|
Function "function expr" |
|
FunctionCallAgg "Function call on aggregate data" |
|
FunctionCallConflict "Function call with reserved keyword as function name" |
|
FunctionCallKeyword "Function call with keyword as function name" |
|
FunctionCallNonKeyword "Function call with nonkeyword as function name" |
|
FunctionNameConflict "Built-in function call names which are conflict with keywords" |
|
FuncDatetimePrec "Function datetime precision" |
|
GlobalScope "The scope of variable" |
|
GrantStmt "Grant statement" |
|
GroupByClause "GROUP BY clause" |
|
HashString "Hashed string" |
|
HavingClause "HAVING clause" |
|
IfExists "If Exists" |
|
IfNotExists "If Not Exists" |
|
IgnoreOptional "IGNORE or empty" |
|
IndexColName "Index column name" |
|
IndexColNameList "List of index column name" |
|
IndexName "index name" |
|
IndexOption "Index Option" |
|
IndexType "index type" |
|
IndexTypeOpt "Optional index type" |
|
InsertIntoStmt "INSERT INTO statement" |
|
InsertValues "Rest part of INSERT/REPLACE INTO statement" |
|
IntoOpt "INTO or EmptyString" |
|
IsolationLevel "Isolation level" |
|
JoinTable "join table" |
|
JoinType "join type" |
|
KeyOrIndex "{KEY|INDEX}" |
|
LikeEscapeOpt "like escape option" |
|
LimitClause "LIMIT clause" |
|
Literal "literal value" |
|
LockTablesStmt "Lock tables statement" |
|
LockType "Table locks type" |
|
logAnd "logical and operator" |
|
logOr "logical or operator" |
|
LowPriorityOptional "LOW_PRIORITY or empty" |
|
name "name" |
|
NationalOpt "National option" |
|
NotOpt "optional NOT" |
|
NowSym "CURRENT_TIMESTAMP/LOCALTIME/LOCALTIMESTAMP/NOW" |
|
NumLiteral "Num/Int/Float/Decimal Literal" |
|
ObjectType "Grant statement object type" |
|
OnDuplicateKeyUpdate "ON DUPLICATE KEY UPDATE value list" |
|
Operand "operand" |
|
OptFull "Full or empty" |
|
OptInteger "Optional Integer keyword" |
|
Order "ORDER BY clause optional collation specification" |
|
OrderBy "ORDER BY clause" |
|
ByItem "BY item" |
|
OrderByOptional "Optional ORDER BY clause optional" |
|
ByList "BY list" |
|
OuterOpt "optional OUTER clause" |
|
QuickOptional "QUICK or empty" |
|
PasswordOpt "Password option" |
|
ColumnPosition "Column position [First|After ColumnName]" |
|
PreparedStmt "PreparedStmt" |
|
PrepareSQL "Prepare statement sql string" |
|
PrimaryExpression "primary expression" |
|
PrimaryFactor "primary expression factor" |
|
Priority "insert statement priority" |
|
PrivElem "Privilege element" |
|
PrivElemList "Privilege element list" |
|
PrivLevel "Privilege scope" |
|
PrivType "Privilege type" |
|
ReferDef "Reference definition" |
|
RegexpSym "REGEXP or RLIKE" |
|
ReplaceIntoStmt "REPLACE INTO statement" |
|
ReplacePriority "replace statement priority" |
|
RollbackStmt "ROLLBACK statement" |
|
RowFormat "Row format option" |
|
SelectLockOpt "FOR UPDATE or LOCK IN SHARE MODE," |
|
SelectStmt "SELECT statement" |
|
SelectStmtCalcFoundRows "SELECT statement optional SQL_CALC_FOUND_ROWS" |
|
SelectStmtDistinct "SELECT statement optional DISTINCT clause" |
|
SelectStmtFieldList "SELECT statement field list" |
|
SelectStmtLimit "SELECT statement optional LIMIT clause" |
|
SelectStmtOpts "Select statement options" |
|
SelectStmtGroup "SELECT statement optional GROUP BY clause" |
|
SetStmt "Set variable statement" |
|
ShowStmt "Show engines/databases/tables/columns/warnings/status statement" |
|
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" |
|
SignedLiteral "Literal or NumLiteral with sign" |
|
Statement "statement" |
|
StatementList "statement list" |
|
StringName "string literal or identifier" |
|
StringList "string list" |
|
ExplainableStmt "explainable statement" |
|
SubSelect "Sub Select" |
|
Symbol "Constraint Symbol" |
|
SystemVariable "System defined variable name" |
|
TableAsName "table alias name" |
|
TableAsNameOpt "table alias name optional" |
|
TableElement "table definition element" |
|
TableElementList "table definition element list" |
|
TableFactor "table factor" |
|
TableLock "Table name and lock type" |
|
TableLockList "Table lock list" |
|
TableName "Table name" |
|
TableNameList "Table name list" |
|
TableOption "create table option" |
|
TableOptionList "create table option list" |
|
TableOptionListOpt "create table option list opt" |
|
TableRef "table reference" |
|
TableRefs "table references" |
|
TimeUnit "Time unit" |
|
TransactionChar "Transaction characteristic" |
|
TransactionChars "Transaction characteristic list" |
|
TrimDirection "Trim string direction" |
|
TruncateTableStmt "TRANSACTION TABLE statement" |
|
UnionOpt "Union Option(empty/ALL/DISTINCT)" |
|
UnionStmt "Union select state ment" |
|
UnionClauseList "Union select clause list" |
|
UnionSelect "Union (select) item" |
|
UnlockTablesStmt "Unlock tables statement" |
|
UpdateStmt "UPDATE statement" |
|
Username "Username" |
|
UserSpec "Username and auth option" |
|
UserSpecList "Username and auth option list" |
|
UserVariable "User defined variable name" |
|
UserVariableList "User defined variable name list" |
|
UseStmt "USE statement" |
|
ValueSym "Value or Values" |
|
VariableAssignment "set variable value" |
|
VariableAssignmentList "set variable value list" |
|
Variable "User or system variable" |
|
WhereClause "WHERE clause" |
|
WhereClauseOptional "Optinal WHERE clause" |
|
|
|
Identifier "identifier or unreserved keyword" |
|
UnReservedKeyword "MySQL unreserved keywords" |
|
NotKeywordToken "Tokens not mysql keyword but treated specially" |
|
|
|
WhenClause "When clause" |
|
WhenClauseList "When clause list" |
|
ElseOpt "Optional else clause" |
|
ExpressionOpt "Optional expression" |
|
|
|
Type "Types" |
|
|
|
NumericType "Numeric types" |
|
IntegerType "Integer 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" |
|
CharsetKw "charset or charater set" |
|
OptCharset "Optional Character setting" |
|
OptCollate "Optional Collate setting" |
|
NUM "numbers" |
|
LengthNum "Field length num(uint64)" |
|
|
|
%token tableRefPriority |
|
|
|
%precedence lowerThanCalcFoundRows |
|
%precedence calcFoundRows |
|
|
|
%precedence lowerThanSetKeyword |
|
%precedence set |
|
|
|
%precedence lowerThanInsertValues |
|
%precedence insertValues |
|
|
|
%left join inner cross left right full |
|
/* A dummy token to force the priority of TableRef production in a join. */ |
|
%left tableRefPriority |
|
%precedence on |
|
%left oror or |
|
%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 |
|
%right not |
|
%right collate |
|
|
|
%precedence lowerThanLeftParen |
|
%precedence '(' |
|
%precedence lowerThanQuick |
|
%precedence quick |
|
%precedence lowerThanEscape |
|
%precedence escape |
|
%precedence lowerThanComma |
|
%precedence ',' |
|
|
|
%start Start |
|
|
|
%% |
|
|
|
Start: |
|
StatementList |
|
| parseExpression Expression |
|
{ |
|
yylex.(*lexer).expr = $2.(ast.ExprNode) |
|
} |
|
|
|
/**************************************AlterTableStmt*************************************** |
|
* See: https://dev.mysql.com/doc/refman/5.7/en/alter-table.html |
|
*******************************************************************************************/ |
|
AlterTableStmt: |
|
"ALTER" IgnoreOptional "TABLE" TableName AlterTableSpecList |
|
{ |
|
$$ = &ast.AlterTableStmt{ |
|
Table: $4.(*ast.TableName), |
|
Specs: $5.([]*ast.AlterTableSpec), |
|
} |
|
} |
|
|
|
AlterTableSpec: |
|
TableOptionListOpt |
|
{ |
|
$$ = &ast.AlterTableSpec{ |
|
Tp: ast.AlterTableOption, |
|
Options:$1.([]*ast.TableOption), |
|
} |
|
} |
|
| "ADD" ColumnKeywordOpt ColumnDef ColumnPosition |
|
{ |
|
$$ = &ast.AlterTableSpec{ |
|
Tp: ast.AlterTableAddColumn, |
|
Column: $3.(*ast.ColumnDef), |
|
Position: $4.(*ast.ColumnPosition), |
|
} |
|
} |
|
| "ADD" Constraint |
|
{ |
|
constraint := $2.(*ast.Constraint) |
|
$$ = &ast.AlterTableSpec{ |
|
Tp: ast.AlterTableAddConstraint, |
|
Constraint: constraint, |
|
} |
|
} |
|
| "DROP" ColumnKeywordOpt ColumnName |
|
{ |
|
$$ = &ast.AlterTableSpec{ |
|
Tp: ast.AlterTableDropColumn, |
|
DropColumn: $3.(*ast.ColumnName), |
|
} |
|
} |
|
| "DROP" "PRIMARY" "KEY" |
|
{ |
|
$$ = &ast.AlterTableSpec{Tp: ast.AlterTableDropPrimaryKey} |
|
} |
|
| "DROP" KeyOrIndex IndexName |
|
{ |
|
$$ = &ast.AlterTableSpec{ |
|
Tp: ast.AlterTableDropIndex, |
|
Name: $3.(string), |
|
} |
|
} |
|
| "DROP" "FOREIGN" "KEY" Symbol |
|
{ |
|
$$ = &ast.AlterTableSpec{ |
|
Tp: ast.AlterTableDropForeignKey, |
|
Name: $4.(string), |
|
} |
|
} |
|
|
|
KeyOrIndex: |
|
"KEY"|"INDEX" |
|
|
|
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), |
|
} |
|
} |
|
|
|
AlterTableSpecList: |
|
AlterTableSpec |
|
{ |
|
$$ = []*ast.AlterTableSpec{$1.(*ast.AlterTableSpec)} |
|
} |
|
| AlterTableSpecList ',' AlterTableSpec |
|
{ |
|
$$ = append($1.([]*ast.AlterTableSpec), $3.(*ast.AlterTableSpec)) |
|
} |
|
|
|
ConstraintKeywordOpt: |
|
{ |
|
$$ = nil |
|
} |
|
| "CONSTRAINT" |
|
{ |
|
$$ = nil |
|
} |
|
| "CONSTRAINT" Symbol |
|
{ |
|
$$ = $2.(string) |
|
} |
|
|
|
Symbol: |
|
Identifier |
|
|
|
/*******************************************************************************************/ |
|
Assignment: |
|
ColumnName eq Expression |
|
{ |
|
$$ = &ast.Assignment{Column: $1.(*ast.ColumnName), Expr:$3.(ast.ExprNode)} |
|
} |
|
|
|
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{} |
|
} |
|
| "START" "TRANSACTION" |
|
{ |
|
$$ = &ast.BeginStmt{} |
|
} |
|
|
|
ColumnDef: |
|
ColumnName Type ColumnOptionListOpt |
|
{ |
|
$$ = &ast.ColumnDef{Name: $1.(*ast.ColumnName), Tp: $2.(*types.FieldType), Options: $3.([]*ast.ColumnOption)} |
|
} |
|
|
|
ColumnName: |
|
Identifier |
|
{ |
|
$$ = &ast.ColumnName{Name: model.NewCIStr($1.(string))} |
|
} |
|
| Identifier '.' Identifier |
|
{ |
|
$$ = &ast.ColumnName{Table: model.NewCIStr($1.(string)), Name: model.NewCIStr($3.(string))} |
|
} |
|
| Identifier '.' Identifier '.' Identifier |
|
{ |
|
$$ = &ast.ColumnName{Schema: model.NewCIStr($1.(string)), Table: model.NewCIStr($3.(string)), Name: model.NewCIStr($5.(string))} |
|
} |
|
|
|
ColumnNameList: |
|
ColumnName |
|
{ |
|
$$ = []*ast.ColumnName{$1.(*ast.ColumnName)} |
|
} |
|
| ColumnNameList ',' ColumnName |
|
{ |
|
$$ = append($1.([]*ast.ColumnName), $3.(*ast.ColumnName)) |
|
} |
|
|
|
ColumnNameListOpt: |
|
/* EMPTY */ |
|
{ |
|
$$ = []*ast.ColumnName{} |
|
} |
|
| ColumnNameList |
|
{ |
|
$$ = $1.([]*ast.ColumnName) |
|
} |
|
|
|
CommitStmt: |
|
"COMMIT" |
|
{ |
|
$$ = &ast.CommitStmt{} |
|
} |
|
|
|
ColumnOption: |
|
"NOT" "NULL" |
|
{ |
|
$$ = &ast.ColumnOption{Tp: ast.ColumnOptionNotNull} |
|
} |
|
| "NULL" |
|
{ |
|
$$ = &ast.ColumnOption{Tp: ast.ColumnOptionNull} |
|
} |
|
| "AUTO_INCREMENT" |
|
{ |
|
$$ = &ast.ColumnOption{Tp: ast.ColumnOptionAutoIncrement} |
|
} |
|
| "PRIMARY" "KEY" |
|
{ |
|
$$ = &ast.ColumnOption{Tp: ast.ColumnOptionPrimaryKey} |
|
} |
|
| "UNIQUE" |
|
{ |
|
$$ = &ast.ColumnOption{Tp: ast.ColumnOptionUniq} |
|
} |
|
| "UNIQUE" "KEY" |
|
{ |
|
$$ = &ast.ColumnOption{Tp: ast.ColumnOptionUniqKey} |
|
} |
|
| "DEFAULT" DefaultValueExpr |
|
{ |
|
$$ = &ast.ColumnOption{Tp: ast.ColumnOptionDefaultValue, Expr: $2.(ast.ExprNode)} |
|
} |
|
| "ON" "UPDATE" NowSym |
|
{ |
|
nowFunc := &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")} |
|
$$ = &ast.ColumnOption{Tp: ast.ColumnOptionOnUpdate, Expr: nowFunc} |
|
} |
|
| "COMMENT" stringLit |
|
{ |
|
$$ = &ast.ColumnOption{Tp: ast.ColumnOptionComment} |
|
} |
|
| "CHECK" '(' Expression ')' |
|
{ |
|
// See: https://dev.mysql.com/doc/refman/5.7/en/create-table.html |
|
// The CHECK clause is parsed but ignored by all storage engines. |
|
$$ = &ast.ColumnOption{} |
|
} |
|
|
|
ColumnOptionList: |
|
ColumnOption |
|
{ |
|
$$ = []*ast.ColumnOption{$1.(*ast.ColumnOption)} |
|
} |
|
| ColumnOptionList ColumnOption |
|
{ |
|
$$ = append($1.([]*ast.ColumnOption), $2.(*ast.ColumnOption)) |
|
} |
|
|
|
ColumnOptionListOpt: |
|
{ |
|
$$ = []*ast.ColumnOption{} |
|
} |
|
| ColumnOptionList |
|
{ |
|
$$ = $1.([]*ast.ColumnOption) |
|
} |
|
|
|
ConstraintElem: |
|
"PRIMARY" "KEY" IndexTypeOpt '(' IndexColNameList ')' IndexOption |
|
{ |
|
c := &ast.Constraint{ |
|
Tp: ast.ConstraintPrimaryKey, |
|
Keys: $5.([]*ast.IndexColName), |
|
} |
|
if $7 != nil { |
|
c.Option = $7.(*ast.IndexOption) |
|
} |
|
if $3 != nil { |
|
if c.Option == nil { |
|
c.Option = &ast.IndexOption{} |
|
} |
|
c.Option.Tp = $3.(model.IndexType) |
|
} |
|
$$ = c |
|
} |
|
| "FULLTEXT" "KEY" IndexName '(' IndexColNameList ')' IndexOption |
|
{ |
|
c := &ast.Constraint{ |
|
Tp: ast.ConstraintFulltext, |
|
Keys: $5.([]*ast.IndexColName), |
|
Name: $3.(string), |
|
} |
|
if $7 != nil { |
|
c.Option = $7.(*ast.IndexOption) |
|
} |
|
$$ = c |
|
} |
|
| "INDEX" IndexName IndexTypeOpt '(' IndexColNameList ')' IndexOption |
|
{ |
|
c := &ast.Constraint{ |
|
Tp: ast.ConstraintIndex, |
|
Keys: $5.([]*ast.IndexColName), |
|
Name: $2.(string), |
|
} |
|
if $7 != nil { |
|
c.Option = $7.(*ast.IndexOption) |
|
} |
|
if $3 != nil { |
|
if c.Option == nil { |
|
c.Option = &ast.IndexOption{} |
|
} |
|
c.Option.Tp = $3.(model.IndexType) |
|
} |
|
$$ = c |
|
} |
|
| "KEY" IndexName IndexTypeOpt '(' IndexColNameList ')' IndexOption |
|
{ |
|
c := &ast.Constraint{ |
|
Tp: ast.ConstraintKey, |
|
Keys: $5.([]*ast.IndexColName), |
|
Name: $2.(string), |
|
} |
|
if $7 != nil { |
|
c.Option = $7.(*ast.IndexOption) |
|
} |
|
if $3 != nil { |
|
if c.Option == nil { |
|
c.Option = &ast.IndexOption{} |
|
} |
|
c.Option.Tp = $3.(model.IndexType) |
|
} |
|
$$ = c |
|
} |
|
| "UNIQUE" IndexName IndexTypeOpt '(' IndexColNameList ')' IndexOption |
|
{ |
|
c := &ast.Constraint{ |
|
Tp: ast.ConstraintUniq, |
|
Keys: $5.([]*ast.IndexColName), |
|
Name: $2.(string), |
|
} |
|
if $7 != nil { |
|
c.Option = $7.(*ast.IndexOption) |
|
} |
|
if $3 != nil { |
|
if c.Option == nil { |
|
c.Option = &ast.IndexOption{} |
|
} |
|
c.Option.Tp = $3.(model.IndexType) |
|
} |
|
$$ = c |
|
} |
|
| "UNIQUE" "INDEX" IndexName IndexTypeOpt '(' IndexColNameList ')' IndexOption |
|
{ |
|
c := &ast.Constraint{ |
|
Tp: ast.ConstraintUniqIndex, |
|
Keys: $6.([]*ast.IndexColName), |
|
Name: $3.(string), |
|
} |
|
if $8 != nil { |
|
c.Option = $8.(*ast.IndexOption) |
|
} |
|
if $4 != nil { |
|
if c.Option == nil { |
|
c.Option = &ast.IndexOption{} |
|
} |
|
c.Option.Tp = $4.(model.IndexType) |
|
} |
|
$$ = c |
|
} |
|
| "UNIQUE" "KEY" IndexName IndexTypeOpt '(' IndexColNameList ')' IndexOption |
|
{ |
|
c := &ast.Constraint{ |
|
Tp: ast.ConstraintUniqKey, |
|
Keys: $6.([]*ast.IndexColName), |
|
Name: $3.(string), |
|
} |
|
if $8 != nil { |
|
c.Option = $8.(*ast.IndexOption) |
|
} |
|
if $4 != nil { |
|
if c.Option == nil { |
|
c.Option = &ast.IndexOption{} |
|
} |
|
c.Option.Tp = $4.(model.IndexType) |
|
} |
|
$$ = c |
|
} |
|
| "FOREIGN" "KEY" IndexName '(' IndexColNameList ')' ReferDef |
|
{ |
|
$$ = &ast.Constraint{ |
|
Tp: ast.ConstraintForeignKey, |
|
Keys: $5.([]*ast.IndexColName), |
|
Name: $3.(string), |
|
Refer: $7.(*ast.ReferenceDef), |
|
} |
|
} |
|
|
|
ReferDef: |
|
"REFERENCES" TableName '(' IndexColNameList ')' |
|
{ |
|
$$ = &ast.ReferenceDef{Table: $2.(*ast.TableName), IndexColNames: $4.([]*ast.IndexColName)} |
|
} |
|
|
|
/* |
|
* 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: |
|
NowSym |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")} |
|
} |
|
| NowSym '(' ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")} |
|
} |
|
| SignedLiteral |
|
|
|
// TODO: Process other three keywords |
|
NowSym: |
|
"CURRENT_TIMESTAMP" |
|
| "LOCALTIME" |
|
| "LOCALTIMESTAMP" |
|
| "NOW" |
|
|
|
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)} |
|
} |
|
|
|
// TODO: support decimal literal |
|
NumLiteral: |
|
intLit |
|
| floatLit |
|
|
|
|
|
CreateIndexStmt: |
|
"CREATE" CreateIndexStmtUnique "INDEX" Identifier "ON" TableName '(' IndexColNameList ')' |
|
{ |
|
$$ = &ast.CreateIndexStmt{ |
|
Unique: $2.(bool), |
|
IndexName: $4.(string), |
|
Table: $6.(*ast.TableName), |
|
IndexColNames: $8.([]*ast.IndexColName), |
|
} |
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
|
|
CreateIndexStmtUnique: |
|
{ |
|
$$ = false |
|
} |
|
| "UNIQUE" |
|
{ |
|
$$ = true |
|
} |
|
|
|
IndexColName: |
|
ColumnName OptFieldLen Order |
|
{ |
|
//Order is parsed but just ignored as MySQL did |
|
$$ = &ast.IndexColName{Column: $1.(*ast.ColumnName), Length: $2.(int)} |
|
} |
|
|
|
IndexColNameList: |
|
{ |
|
$$ = []*ast.IndexColName{} |
|
} |
|
| IndexColName |
|
{ |
|
$$ = []*ast.IndexColName{$1.(*ast.IndexColName)} |
|
} |
|
| IndexColNameList ',' IndexColName |
|
{ |
|
$$ = append($1.([]*ast.IndexColName), $3.(*ast.IndexColName)) |
|
} |
|
|
|
|
|
|
|
/******************************************************************* |
|
* |
|
* Create Database Statement |
|
* CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name |
|
* [create_specification] ... |
|
* |
|
* create_specification: |
|
* [DEFAULT] CHARACTER SET [=] charset_name |
|
* | [DEFAULT] COLLATE [=] collation_name |
|
*******************************************************************/ |
|
CreateDatabaseStmt: |
|
"CREATE" DatabaseSym IfNotExists DBName DatabaseOptionListOpt |
|
{ |
|
$$ = &ast.CreateDatabaseStmt{ |
|
IfNotExists: $3.(bool), |
|
Name: $4.(string), |
|
Options: $5.([]*ast.DatabaseOption), |
|
} |
|
|
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
|
|
DBName: |
|
Identifier |
|
|
|
DatabaseOption: |
|
DefaultKwdOpt CharsetKw EqOpt StringName |
|
{ |
|
$$ = &ast.DatabaseOption{Tp: ast.DatabaseOptionCharset, Value: $4.(string)} |
|
} |
|
| DefaultKwdOpt "COLLATE" EqOpt StringName |
|
{ |
|
$$ = &ast.DatabaseOption{Tp: ast.DatabaseOptionCollate, Value: $4.(string)} |
|
} |
|
|
|
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" "TABLE" IfNotExists TableName '(' TableElementList ')' TableOptionListOpt |
|
{ |
|
tes := $6.([]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) |
|
} |
|
} |
|
if len(columnDefs) == 0 { |
|
yylex.(*lexer).err("Column Definition List can't be empty.") |
|
return 1 |
|
} |
|
$$ = &ast.CreateTableStmt{ |
|
Table: $4.(*ast.TableName), |
|
IfNotExists: $3.(bool), |
|
Cols: columnDefs, |
|
Constraints: constraints, |
|
Options: $8.([]*ast.TableOption), |
|
} |
|
} |
|
|
|
Default: |
|
"DEFAULT" Expression |
|
{ |
|
$$ = $2 |
|
} |
|
|
|
DefaultOpt: |
|
{ |
|
$$ = nil |
|
} |
|
| Default |
|
|
|
DefaultKwdOpt: |
|
{} |
|
| "DEFAULT" |
|
|
|
/****************************************************************** |
|
* 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" LowPriorityOptional QuickOptional IgnoreOptional "FROM" TableName WhereClauseOptional OrderByOptional LimitClause |
|
{ |
|
// Single Table |
|
join := &ast.Join{Left: &ast.TableSource{Source: $6.(ast.ResultSetNode)}, Right: nil} |
|
x := &ast.DeleteStmt{ |
|
TableRefs: &ast.TableRefsClause{TableRefs: join}, |
|
LowPriority: $2.(bool), |
|
Quick: $3.(bool), |
|
Ignore: $4.(bool), |
|
} |
|
if $7 != nil { |
|
x.Where = $7.(ast.ExprNode) |
|
} |
|
if $8 != nil { |
|
x.Order = $8.(*ast.OrderByClause) |
|
} |
|
if $9 != nil { |
|
x.Limit = $9.(*ast.Limit) |
|
} |
|
|
|
$$ = x |
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
| "DELETE" LowPriorityOptional QuickOptional IgnoreOptional TableNameList "FROM" TableRefs WhereClauseOptional |
|
{ |
|
// Multiple Table |
|
x := &ast.DeleteStmt{ |
|
LowPriority: $2.(bool), |
|
Quick: $3.(bool), |
|
Ignore: $4.(bool), |
|
IsMultiTable: true, |
|
BeforeFrom: true, |
|
Tables: &ast.DeleteTableList{Tables: $5.([]*ast.TableName)}, |
|
TableRefs: &ast.TableRefsClause{TableRefs: $7.(*ast.Join)}, |
|
} |
|
if $8 != nil { |
|
x.Where = $8.(ast.ExprNode) |
|
} |
|
$$ = x |
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
| "DELETE" LowPriorityOptional QuickOptional IgnoreOptional "FROM" TableNameList "USING" TableRefs WhereClauseOptional |
|
{ |
|
// Multiple Table |
|
x := &ast.DeleteStmt{ |
|
LowPriority: $2.(bool), |
|
Quick: $3.(bool), |
|
Ignore: $4.(bool), |
|
IsMultiTable: true, |
|
Tables: &ast.DeleteTableList{Tables: $6.([]*ast.TableName)}, |
|
TableRefs: &ast.TableRefsClause{TableRefs: $8.(*ast.Join)}, |
|
} |
|
if $9 != nil { |
|
x.Where = $9.(ast.ExprNode) |
|
} |
|
$$ = x |
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
|
|
DatabaseSym: |
|
"DATABASE" | "SCHEMA" |
|
|
|
DropDatabaseStmt: |
|
"DROP" DatabaseSym IfExists DBName |
|
{ |
|
$$ = &ast.DropDatabaseStmt{IfExists: $3.(bool), Name: $4.(string)} |
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
|
|
DropIndexStmt: |
|
"DROP" "INDEX" IfExists Identifier "ON" TableName |
|
{ |
|
$$ = &ast.DropIndexStmt{IfExists: $3.(bool), IndexName: $4.(string), Table: $6.(*ast.TableName)} |
|
} |
|
|
|
DropTableStmt: |
|
"DROP" TableOrTables TableNameList |
|
{ |
|
$$ = &ast.DropTableStmt{Tables: $3.([]*ast.TableName)} |
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
| "DROP" TableOrTables "IF" "EXISTS" TableNameList |
|
{ |
|
$$ = &ast.DropTableStmt{IfExists: true, Tables: $5.([]*ast.TableName)} |
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
|
|
TableOrTables: |
|
"TABLE" |
|
| "TABLES" |
|
|
|
EqOpt: |
|
{ |
|
} |
|
| eq |
|
{ |
|
} |
|
|
|
EmptyStmt: |
|
/* EMPTY */ |
|
{ |
|
$$ = nil |
|
} |
|
|
|
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.(ast.StmtNode)} |
|
} |
|
|
|
LengthNum: |
|
NUM |
|
{ |
|
switch v := $1.(type) { |
|
case int64: |
|
$$ = uint64(v) |
|
case uint64: |
|
$$ = uint64(v) |
|
} |
|
} |
|
|
|
NUM: |
|
intLit |
|
|
|
Expression: |
|
Expression logOr Expression %prec oror |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.OrOr, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| Expression "XOR" Expression %prec xor |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.LogicXor, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| Expression logAnd Expression %prec andand |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.AndAnd, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| "NOT" Expression %prec not |
|
{ |
|
$$ = &ast.UnaryOperationExpr{Op: opcode.Not, V: $2.(ast.ExprNode)} |
|
} |
|
| Factor "IS" NotOpt trueKwd %prec is |
|
{ |
|
$$ = &ast.IsTruthExpr{Expr:$1.(ast.ExprNode), Not: $3.(bool), True: int64(1)} |
|
} |
|
| Factor "IS" NotOpt falseKwd %prec is |
|
{ |
|
$$ = &ast.IsTruthExpr{Expr:$1.(ast.ExprNode), Not: $3.(bool), True: int64(0)} |
|
} |
|
| Factor "IS" NotOpt "UNKNOWN" %prec is |
|
{ |
|
/* https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_is */ |
|
$$ = &ast.IsNullExpr{Expr: $1.(ast.ExprNode), Not: $3.(bool)} |
|
} |
|
| Factor |
|
|
|
|
|
logOr: |
|
"||" |
|
{ |
|
} |
|
| "OR" |
|
{ |
|
} |
|
|
|
logAnd: |
|
"&&" |
|
{ |
|
} |
|
| "AND" |
|
{ |
|
} |
|
|
|
name: |
|
Identifier |
|
|
|
ExpressionList: |
|
Expression |
|
{ |
|
$$ = []ast.ExprNode{$1.(ast.ExprNode)} |
|
} |
|
| ExpressionList ',' Expression |
|
{ |
|
$$ = append($1.([]ast.ExprNode), $3.(ast.ExprNode)) |
|
} |
|
|
|
ExpressionListOpt: |
|
{ |
|
$$ = []ast.ExprNode{} |
|
} |
|
| ExpressionList |
|
|
|
Factor: |
|
Factor "IS" NotOpt "NULL" %prec is |
|
{ |
|
$$ = &ast.IsNullExpr{Expr: $1.(ast.ExprNode), Not: $3.(bool)} |
|
} |
|
| Factor CompareOp PredicateExpr %prec eq |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: $2.(opcode.Op), L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| Factor CompareOp AnyOrAll SubSelect %prec eq |
|
{ |
|
$$ = &ast.CompareSubqueryExpr{Op: $2.(opcode.Op), L: $1.(ast.ExprNode), R: $4.(*ast.SubqueryExpr), All: $3.(bool)} |
|
} |
|
| PredicateExpr |
|
|
|
CompareOp: |
|
">=" |
|
{ |
|
$$ = opcode.GE |
|
} |
|
| '>' |
|
{ |
|
$$ = opcode.GT |
|
} |
|
| "<=" |
|
{ |
|
$$ = opcode.LE |
|
} |
|
| '<' |
|
{ |
|
$$ = opcode.LT |
|
} |
|
| "!=" |
|
{ |
|
$$ = opcode.NE |
|
} |
|
| "<>" |
|
{ |
|
$$ = opcode.NE |
|
} |
|
| "=" |
|
{ |
|
$$ = opcode.EQ |
|
} |
|
| "<=>" |
|
{ |
|
$$ = opcode.NullEQ |
|
} |
|
|
|
AnyOrAll: |
|
"ANY" |
|
{ |
|
$$ = false |
|
} |
|
| "SOME" |
|
{ |
|
$$ = false |
|
} |
|
| "ALL" |
|
{ |
|
$$ = true |
|
} |
|
|
|
PredicateExpr: |
|
PrimaryFactor NotOpt "IN" '(' ExpressionList ')' |
|
{ |
|
$$ = &ast.PatternInExpr{Expr: $1.(ast.ExprNode), Not: $2.(bool), List: $5.([]ast.ExprNode)} |
|
} |
|
| PrimaryFactor NotOpt "IN" SubSelect |
|
{ |
|
$$ = &ast.PatternInExpr{Expr: $1.(ast.ExprNode), Not: $2.(bool), Sel: $4.(*ast.SubqueryExpr)} |
|
} |
|
| PrimaryFactor NotOpt "BETWEEN" PrimaryFactor "AND" PredicateExpr |
|
{ |
|
$$ = &ast.BetweenExpr{ |
|
Expr: $1.(ast.ExprNode), |
|
Left: $4.(ast.ExprNode), |
|
Right: $6.(ast.ExprNode), |
|
Not: $2.(bool), |
|
} |
|
} |
|
| PrimaryFactor NotOpt "LIKE" PrimaryExpression LikeEscapeOpt |
|
{ |
|
escape := $5.(string) |
|
if len(escape) > 1 { |
|
yylex.(*lexer).errf("Incorrect arguments %s to ESCAPE", escape) |
|
return 1 |
|
} else if len(escape) == 0 { |
|
escape = "\\" |
|
} |
|
$$ = &ast.PatternLikeExpr{ |
|
Expr: $1.(ast.ExprNode), |
|
Pattern: $4.(ast.ExprNode), |
|
Not: $2.(bool), |
|
Escape: escape[0], |
|
} |
|
} |
|
| PrimaryFactor NotOpt RegexpSym PrimaryExpression |
|
{ |
|
$$ = &ast.PatternRegexpExpr{Expr: $1.(ast.ExprNode), Pattern: $4.(ast.ExprNode), Not: $2.(bool)} |
|
} |
|
| PrimaryFactor |
|
|
|
RegexpSym: |
|
"REGEXP" |
|
| "RLIKE" |
|
|
|
LikeEscapeOpt: |
|
%prec lowerThanEscape |
|
{ |
|
$$ = "\\" |
|
} |
|
| "ESCAPE" stringLit |
|
{ |
|
$$ = $2 |
|
} |
|
|
|
NotOpt: |
|
{ |
|
$$ = false |
|
} |
|
| "NOT" |
|
{ |
|
$$ = true |
|
} |
|
|
|
Field: |
|
'*' |
|
{ |
|
$$ = &ast.SelectField{WildCard: &ast.WildCardField{}} |
|
} |
|
| Identifier '.' '*' |
|
{ |
|
wildCard := &ast.WildCardField{Table: model.NewCIStr($1.(string))} |
|
$$ = &ast.SelectField{WildCard: wildCard} |
|
} |
|
| Identifier '.' Identifier '.' '*' |
|
{ |
|
wildCard := &ast.WildCardField{Schema: model.NewCIStr($1.(string)), Table: model.NewCIStr($3.(string))} |
|
$$ = &ast.SelectField{WildCard: wildCard} |
|
} |
|
| Expression FieldAsNameOpt |
|
{ |
|
expr := $1.(ast.ExprNode) |
|
asName := $2.(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 = yylex.(*lexer).startOffset(yyS[yypt].offset) |
|
$$ = []*ast.SelectField{field} |
|
} |
|
| FieldList ',' Field |
|
{ |
|
|
|
fl := $1.([]*ast.SelectField) |
|
last := fl[len(fl)-1] |
|
l := yylex.(*lexer) |
|
if last.Expr != nil && last.AsName.O == "" { |
|
lastEnd := l.endOffset(yyS[yypt-1].offset) |
|
last.SetText(l.src[last.Offset:lastEnd]) |
|
} |
|
newField := $3.(*ast.SelectField) |
|
newField.Offset = l.startOffset(yyS[yypt].offset) |
|
$$ = append(fl, newField) |
|
} |
|
|
|
GroupByClause: |
|
"GROUP" "BY" ByList |
|
{ |
|
$$ = &ast.GroupByClause{Items: $3.([]*ast.ByItem)} |
|
} |
|
|
|
HavingClause: |
|
{ |
|
$$ = nil |
|
} |
|
| "HAVING" Expression |
|
{ |
|
$$ = &ast.HavingClause{Expr: $2.(ast.ExprNode)} |
|
} |
|
|
|
IfExists: |
|
{ |
|
$$ = false |
|
} |
|
| "IF" "EXISTS" |
|
{ |
|
$$ = true |
|
} |
|
|
|
IfNotExists: |
|
{ |
|
$$ = false |
|
} |
|
| "IF" "NOT" "EXISTS" |
|
{ |
|
$$ = true |
|
} |
|
|
|
|
|
IgnoreOptional: |
|
{ |
|
$$ = false |
|
} |
|
| "IGNORE" |
|
{ |
|
$$ = true |
|
} |
|
|
|
IndexName: |
|
{ |
|
$$ = "" |
|
} |
|
| Identifier |
|
{ |
|
//"index name" |
|
$$ = $1.(string) |
|
} |
|
|
|
IndexOption: |
|
{ |
|
$$ = nil |
|
} |
|
| "KEY_BLOCK_SIZE" EqOpt LengthNum |
|
{ |
|
$$ = &ast.IndexOption{ |
|
KeyBlockSize: $1.(uint64), |
|
} |
|
} |
|
| IndexType |
|
{ |
|
$$ = &ast.IndexOption { |
|
Tp: $1.(model.IndexType), |
|
} |
|
} |
|
| "COMMENT" stringLit |
|
{ |
|
$$ = &ast.IndexOption { |
|
Comment: $2.(string), |
|
} |
|
} |
|
|
|
IndexType: |
|
"USING" "BTREE" |
|
{ |
|
$$ = model.IndexTypeBtree |
|
} |
|
| "USING" "HASH" |
|
{ |
|
$$ = model.IndexTypeHash |
|
} |
|
|
|
IndexTypeOpt: |
|
{ |
|
$$ = nil |
|
} |
|
| IndexType |
|
{ |
|
$$ = $1 |
|
} |
|
|
|
/**********************************Identifier********************************************/ |
|
Identifier: |
|
identifier | UnReservedKeyword | NotKeywordToken |
|
|
|
UnReservedKeyword: |
|
"AUTO_INCREMENT" | "AFTER" | "AVG" | "BEGIN" | "BIT" | "BOOL" | "BOOLEAN" | "BTREE" | "CHARSET" | "COLUMNS" | "COMMIT" | "COMPACT" | "COMPRESSED" |
|
| "DATE" | "DATETIME" | "DEALLOCATE" | "DO" | "DYNAMIC" | "END" | "ENGINE" | "ENGINES" | "EXECUTE" | "FIRST" | "FIXED" | "FULL" | "HASH" |
|
| "LOCAL" | "NAMES" | "OFFSET" | "PASSWORD" %prec lowerThanEq | "PREPARE" | "QUICK" | "REDUNDANT" | "ROLLBACK" | "SESSION" | "SIGNED" |
|
| "START" | "STATUS" | "GLOBAL" | "TABLES"| "TEXT" | "TIME" | "TIMESTAMP" | "TRANSACTION" | "TRUNCATE" | "UNKNOWN" |
|
| "VALUE" | "WARNINGS" | "YEAR" | "MODE" | "WEEK" | "ANY" | "SOME" | "USER" | "IDENTIFIED" | "COLLATION" |
|
| "COMMENT" | "AVG_ROW_LENGTH" | "CONNECTION" | "CHECKSUM" | "COMPRESSION" | "KEY_BLOCK_SIZE" | "MAX_ROWS" | "MIN_ROWS" |
|
| "NATIONAL" | "ROW" | "ROW_FORMAT" | "QUARTER" | "ESCAPE" | "GRANTS" | "FIELDS" | "TRIGGERS" | "DELAY_KEY_WRITE" | "ISOLATION" |
|
| "REPEATABLE" | "COMMITTED" | "UNCOMMITTED" | "ONLY" | "SERIALIZABLE" | "LEVEL" | "VARIABLES" |
|
|
|
NotKeywordToken: |
|
"ABS" | "ADDDATE" | "ADMIN" | "COALESCE" | "CONCAT" | "CONCAT_WS" | "CONNECTION_ID" | "CUR_TIME"| "COUNT" | "DAY" |
|
| "DATE_ADD" | "DATE_SUB" | "DAYNAME" | "DAYOFMONTH" | "DAYOFWEEK" | "DAYOFYEAR" | "FOUND_ROWS" | "GROUP_CONCAT"| "HOUR" |
|
| "IFNULL" | "LENGTH" | "LOCATE" | "MAX" | "MICROSECOND" | "MIN" | "MINUTE" | "NULLIF" | "MONTH" | "NOW" | "POW" |
|
| "POWER" | "RAND" | "SECOND" | "SQL_CALC_FOUND_ROWS" | "SUBDATE" | "SUBSTRING" %prec lowerThanLeftParen |
|
| "SUBSTRING_INDEX" | "SUM" | "TRIM" | "VERSION" | "WEEKDAY" | "WEEKOFYEAR" | "YEARWEEK" |
|
|
|
/************************************************************************************ |
|
* |
|
* Insert Statments |
|
* |
|
* TODO: support PARTITION |
|
**********************************************************************************/ |
|
InsertIntoStmt: |
|
"INSERT" Priority IgnoreOptional IntoOpt TableName InsertValues OnDuplicateKeyUpdate |
|
{ |
|
x := $6.(*ast.InsertStmt) |
|
x.Priority = $2.(int) |
|
// 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 |
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
|
|
IntoOpt: |
|
{ |
|
} |
|
| "INTO" |
|
{ |
|
} |
|
|
|
InsertValues: |
|
'(' ColumnNameListOpt ')' ValueSym ExpressionListList |
|
{ |
|
$$ = &ast.InsertStmt{ |
|
Columns: $2.([]*ast.ColumnName), |
|
Lists: $5.([][]ast.ExprNode), |
|
} |
|
} |
|
| '(' ColumnNameListOpt ')' SelectStmt |
|
{ |
|
$$ = &ast.InsertStmt{Columns: $2.([]*ast.ColumnName), Select: $4.(*ast.SelectStmt)} |
|
} |
|
| '(' ColumnNameListOpt ')' UnionStmt |
|
{ |
|
$$ = &ast.InsertStmt{Columns: $2.([]*ast.ColumnName), Select: $4.(*ast.UnionStmt)} |
|
} |
|
| ValueSym ExpressionListList %prec insertValues |
|
{ |
|
$$ = &ast.InsertStmt{Lists: $2.([][]ast.ExprNode)} |
|
} |
|
| 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" |
|
|
|
ExpressionListList: |
|
'(' ')' |
|
{ |
|
$$ = [][]ast.ExprNode{[]ast.ExprNode{}} |
|
} |
|
| '(' ')' ',' ExpressionListList |
|
{ |
|
$$ = append([][]ast.ExprNode{[]ast.ExprNode{}}, $4.([][]ast.ExprNode)...) |
|
} |
|
| '(' ExpressionList ')' |
|
{ |
|
$$ = [][]ast.ExprNode{$2.([]ast.ExprNode)} |
|
} |
|
| '(' ExpressionList ')' ',' ExpressionListList |
|
{ |
|
$$ = append([][]ast.ExprNode{$2.([]ast.ExprNode)}, $5.([][]ast.ExprNode)...) |
|
} |
|
|
|
ColumnSetValue: |
|
ColumnName eq Expression |
|
{ |
|
$$ = &ast.Assignment{ |
|
Column: $1.(*ast.ColumnName), |
|
Expr: $3.(ast.ExprNode), |
|
} |
|
} |
|
|
|
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" ReplacePriority IntoOpt TableName InsertValues |
|
{ |
|
x := $5.(*ast.InsertStmt) |
|
x.IsReplace = true |
|
x.Priority = $2.(int) |
|
ts := &ast.TableSource{Source: $4.(*ast.TableName)} |
|
x.Table = &ast.TableRefsClause{TableRefs: &ast.Join{Left: ts}} |
|
$$ = x |
|
} |
|
|
|
ReplacePriority: |
|
{ |
|
$$ = ast.NoPriority |
|
} |
|
| "LOW_PRIORITY" |
|
{ |
|
$$ = ast.LowPriority |
|
} |
|
| "DELAYED" |
|
{ |
|
$$ = ast.DelayedPriority |
|
} |
|
|
|
/***********************************Replace Statments END************************************/ |
|
|
|
Literal: |
|
"false" |
|
{ |
|
$$ = int64(0) |
|
} |
|
| "NULL" |
|
| "true" |
|
{ |
|
$$ = int64(1) |
|
} |
|
| floatLit |
|
| intLit |
|
| stringLit |
|
{ |
|
tp := types.NewFieldType(mysql.TypeString) |
|
l := yylex.(*lexer) |
|
tp.Charset, tp.Collate = l.GetCharsetInfo() |
|
expr := ast.NewValueExpr($1) |
|
expr.SetType(tp) |
|
$$ = expr |
|
} |
|
| "UNDERSCORE_CHARSET" stringLit |
|
{ |
|
// See: https://dev.mysql.com/doc/refman/5.7/en/charset-literal.html |
|
tp := types.NewFieldType(mysql.TypeString) |
|
tp.Charset = $1.(string) |
|
co, err := charset.GetDefaultCollation(tp.Charset) |
|
if err != nil { |
|
l := yylex.(*lexer) |
|
l.errf("Get collation error for charset: %s", tp.Charset) |
|
return 1 |
|
} |
|
tp.Collate = co |
|
expr := ast.NewValueExpr($2) |
|
expr.SetType(tp) |
|
$$ = expr |
|
} |
|
| hexLit |
|
| bitLit |
|
|
|
Operand: |
|
Literal |
|
{ |
|
$$ = ast.NewValueExpr($1) |
|
} |
|
| ColumnName |
|
{ |
|
$$ = &ast.ColumnNameExpr{Name: $1.(*ast.ColumnName)} |
|
} |
|
| '(' Expression ')' |
|
{ |
|
l := yylex.(*lexer) |
|
startOffset := l.startOffset(yyS[yypt-1].offset) |
|
endOffset := l.endOffset(yyS[yypt].offset) |
|
expr := $2.(ast.ExprNode) |
|
expr.SetText(l.src[startOffset:endOffset]) |
|
$$ = &ast.ParenthesesExpr{Expr: expr} |
|
} |
|
| "DEFAULT" %prec lowerThanLeftParen |
|
{ |
|
$$ = &ast.DefaultExpr{} |
|
} |
|
| "DEFAULT" '(' ColumnName ')' |
|
{ |
|
$$ = &ast.DefaultExpr{Name: $3.(*ast.ColumnName)} |
|
} |
|
| Variable |
|
{ |
|
$$ = $1 |
|
} |
|
| "PLACEHOLDER" |
|
{ |
|
$$ = &ast.ParamMarkerExpr{ |
|
Offset: yyS[yypt].offset, |
|
} |
|
} |
|
| "ROW" '(' Expression ',' ExpressionList ')' |
|
{ |
|
values := append([]ast.ExprNode{$3.(ast.ExprNode)}, $5.([]ast.ExprNode)...) |
|
$$ = &ast.RowExpr{Values: values} |
|
} |
|
| '(' Expression ',' ExpressionList ')' |
|
{ |
|
values := append([]ast.ExprNode{$2.(ast.ExprNode)}, $4.([]ast.ExprNode)...) |
|
$$ = &ast.RowExpr{Values: values} |
|
} |
|
| "EXISTS" SubSelect |
|
{ |
|
$$ = &ast.ExistsSubqueryExpr{Sel: $2.(*ast.SubqueryExpr)} |
|
} |
|
|
|
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.(ast.ExprNode), Desc: $2.(bool)} |
|
} |
|
|
|
Order: |
|
/* EMPTY */ |
|
{ |
|
$$ = false // ASC by default |
|
} |
|
| "ASC" |
|
{ |
|
$$ = false |
|
} |
|
| "DESC" |
|
{ |
|
$$ = true |
|
} |
|
|
|
OrderByOptional: |
|
{ |
|
$$ = nil |
|
} |
|
| OrderBy |
|
{ |
|
$$ = $1 |
|
} |
|
|
|
PrimaryExpression: |
|
Operand |
|
| Function |
|
| SubSelect |
|
| '!' PrimaryExpression %prec neg |
|
{ |
|
$$ = &ast.UnaryOperationExpr{Op: opcode.Not, V: $2.(ast.ExprNode)} |
|
} |
|
| '~' PrimaryExpression %prec neg |
|
{ |
|
$$ = &ast.UnaryOperationExpr{Op: opcode.BitNeg, V: $2.(ast.ExprNode)} |
|
} |
|
| '-' PrimaryExpression %prec neg |
|
{ |
|
$$ = &ast.UnaryOperationExpr{Op: opcode.Minus, V: $2.(ast.ExprNode)} |
|
} |
|
| '+' PrimaryExpression %prec neg |
|
{ |
|
$$ = &ast.UnaryOperationExpr{Op: opcode.Plus, V: $2.(ast.ExprNode)} |
|
} |
|
| "BINARY" PrimaryExpression %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.(ast.ExprNode), |
|
Tp: x, |
|
FunctionType: ast.CastBinaryOperator, |
|
} |
|
} |
|
| PrimaryExpression "COLLATE" StringName %prec neg |
|
{ |
|
// TODO: Create a builtin function hold expr and collation. When do evaluation, convert expr result using the collation. |
|
$$ = $1 |
|
} |
|
|
|
Function: |
|
FunctionCallKeyword |
|
| FunctionCallNonKeyword |
|
| FunctionCallConflict |
|
| FunctionCallAgg |
|
|
|
FunctionNameConflict: |
|
"DATABASE" | "SCHEMA" | "IF" | "LEFT" | "REPEAT" | "CURRENT_USER" | "CURRENT_DATE" | "VERSION" |
|
|
|
FunctionCallConflict: |
|
FunctionNameConflict '(' ExpressionListOpt ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: $3.([]ast.ExprNode)} |
|
} |
|
| "CURRENT_USER" |
|
{ |
|
// See: https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_current-user |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string))} |
|
} |
|
| "CURRENT_DATE" |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string))} |
|
} |
|
|
|
DistinctOpt: |
|
{ |
|
$$ = false |
|
} |
|
| "ALL" |
|
{ |
|
$$ = false |
|
} |
|
| "DISTINCT" |
|
{ |
|
$$ = true |
|
} |
|
| "DISTINCT" "ALL" |
|
{ |
|
$$ = true |
|
} |
|
|
|
FunctionCallKeyword: |
|
"CAST" '(' Expression "AS" CastType ')' |
|
{ |
|
/* See: https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_cast */ |
|
$$ = &ast.FuncCastExpr{ |
|
Expr: $3.(ast.ExprNode), |
|
Tp: $5.(*types.FieldType), |
|
FunctionType: ast.CastFunction, |
|
} |
|
} |
|
| "CASE" ExpressionOpt WhenClauseList ElseOpt "END" |
|
{ |
|
x := &ast.CaseExpr{WhenClauses: $3.([]*ast.WhenClause)} |
|
if $2 != nil { |
|
x.Value = $2.(ast.ExprNode) |
|
} |
|
if $4 != nil { |
|
x.ElseClause = $4.(ast.ExprNode) |
|
} |
|
$$ = x |
|
} |
|
| "CONVERT" '(' Expression "USING" StringName ')' |
|
{ |
|
// See: https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert |
|
charset := ast.NewValueExpr($5) |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$3.(ast.ExprNode), charset}, |
|
} |
|
} |
|
| "CONVERT" '(' Expression ',' CastType ')' |
|
{ |
|
// See: https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert |
|
$$ = &ast.FuncCastExpr{ |
|
Expr: $3.(ast.ExprNode), |
|
Tp: $5.(*types.FieldType), |
|
FunctionType: ast.CastConvertFunction, |
|
} |
|
} |
|
| "DATE" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "USER" '(' ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string))} |
|
} |
|
| "VALUES" '(' ColumnName ')' %prec lowerThanInsertValues |
|
{ |
|
// TODO: support qualified identifier for column_name |
|
$$ = &ast.ValuesExpr{Column: &ast.ColumnNameExpr{Name: $3.(*ast.ColumnName)}} |
|
} |
|
| "WEEK" '(' ExpressionList ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: $3.([]ast.ExprNode)} |
|
} |
|
| "YEAR" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName:model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
|
|
FunctionCallNonKeyword: |
|
"COALESCE" '(' ExpressionList ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: $3.([]ast.ExprNode)} |
|
} |
|
| "CURDATE" '(' ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string))} |
|
} |
|
| "CUR_TIME" '(' ExpressionOpt ')' |
|
{ |
|
args := []ast.ExprNode{} |
|
if $3 != nil { |
|
args = append(args, $3.(ast.ExprNode)) |
|
} |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: args} |
|
} |
|
| "CURRENT_TIME" FuncDatetimePrec |
|
{ |
|
args := []ast.ExprNode{} |
|
if $2 != nil { |
|
args = append(args, $2.(ast.ExprNode)) |
|
} |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: args} |
|
} |
|
| "CURRENT_TIMESTAMP" FuncDatetimePrec |
|
{ |
|
args := []ast.ExprNode{} |
|
if $2 != nil { |
|
args = append(args, $2.(ast.ExprNode)) |
|
} |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: args} |
|
} |
|
| "ABS" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "CONCAT" '(' ExpressionList ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: $3.([]ast.ExprNode)} |
|
} |
|
| "CONCAT_WS" '(' ExpressionList ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: $3.([]ast.ExprNode)} |
|
} |
|
| "DAY" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "DAYNAME" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "DAYOFWEEK" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "DAYOFMONTH" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "DAYOFYEAR" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| DateArithOpt '(' Expression ',' "INTERVAL" Expression TimeUnit ')' |
|
{ |
|
op := ast.NewValueExpr($1) |
|
dateArithInterval := ast.NewValueExpr( |
|
ast.DateArithInterval{ |
|
Unit: $7.(string), |
|
Interval: $6.(ast.ExprNode), |
|
}, |
|
) |
|
|
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr("DATE_ARITH"), |
|
Args: []ast.ExprNode{ |
|
op, |
|
$3.(ast.ExprNode), |
|
dateArithInterval, |
|
}, |
|
} |
|
} |
|
| DateArithMultiFormsOpt '(' Expression ',' DateArithInterval')' |
|
{ |
|
op := ast.NewValueExpr($1) |
|
dateArithInterval := ast.NewValueExpr($5) |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr("DATE_ARITH"), |
|
Args: []ast.ExprNode{ |
|
op, |
|
$3.(ast.ExprNode), |
|
dateArithInterval, |
|
}, |
|
} |
|
} |
|
| "EXTRACT" '(' TimeUnit "FROM" Expression ')' |
|
{ |
|
timeUnit := ast.NewValueExpr($3) |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{timeUnit, $5.(ast.ExprNode)}, |
|
} |
|
} |
|
| "FOUND_ROWS" '(' ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string))} |
|
} |
|
| "HOUR" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "IFNULL" '(' ExpressionList ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: $3.([]ast.ExprNode)} |
|
} |
|
| "LENGTH" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "LOCATE" '(' Expression ',' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode)}, |
|
} |
|
} |
|
| "LOCATE" '(' Expression ',' Expression ',' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode), $7.(ast.ExprNode)}, |
|
} |
|
} |
|
| "LOWER" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "MICROSECOND" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "MINUTE" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "MONTH" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "NOW" '(' ExpressionOpt ')' |
|
{ |
|
args := []ast.ExprNode{} |
|
if $3 != nil { |
|
args = append(args, $3.(ast.ExprNode)) |
|
} |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: args} |
|
} |
|
| "NULLIF" '(' ExpressionList ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: $3.([]ast.ExprNode)} |
|
} |
|
| "POW" '(' Expression ',' Expression ')' |
|
{ |
|
args := []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode)} |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: args} |
|
} |
|
| "POWER" '(' Expression ',' Expression ')' |
|
{ |
|
args := []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode)} |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: args} |
|
} |
|
| "RAND" '(' ExpressionOpt ')' |
|
{ |
|
|
|
args := []ast.ExprNode{} |
|
if $3 != nil { |
|
args = append(args, $3.(ast.ExprNode)) |
|
} |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: args} |
|
} |
|
| "REPLACE" '(' Expression ',' Expression ',' Expression ')' |
|
{ |
|
args := []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode), $7.(ast.ExprNode)} |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: args} |
|
} |
|
| "SECOND" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "STRCMP" '(' Expression ',' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode)}} |
|
} |
|
| "SUBSTRING" '(' Expression ',' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode)}, |
|
} |
|
} |
|
| "SUBSTRING" '(' Expression "FROM" Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode)}, |
|
} |
|
} |
|
| "SUBSTRING" '(' Expression ',' Expression ',' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode), $7.(ast.ExprNode)}, |
|
} |
|
} |
|
| "SUBSTRING" '(' Expression "FROM" Expression "FOR" Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode), $7.(ast.ExprNode)}, |
|
} |
|
} |
|
| "SUBSTRING_INDEX" '(' Expression ',' Expression ',' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode), $7.(ast.ExprNode)}, |
|
} |
|
} |
|
| "SYSDATE" '(' ExpressionOpt ')' |
|
{ |
|
args := []ast.ExprNode{} |
|
if $3 != nil { |
|
args = append(args, $3.(ast.ExprNode)) |
|
} |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: args} |
|
} |
|
| "TRIM" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$3.(ast.ExprNode)}, |
|
} |
|
} |
|
| "TRIM" '(' Expression "FROM" Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$5.(ast.ExprNode), $3.(ast.ExprNode)}, |
|
} |
|
} |
|
| "TRIM" '(' TrimDirection "FROM" Expression ')' |
|
{ |
|
nilVal := ast.NewValueExpr(nil) |
|
direction := ast.NewValueExpr($3) |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$5.(ast.ExprNode), nilVal, direction}, |
|
} |
|
} |
|
| "TRIM" '(' TrimDirection Expression "FROM" Expression ')' |
|
{ |
|
direction := ast.NewValueExpr($3) |
|
$$ = &ast.FuncCallExpr{ |
|
FnName: model.NewCIStr($1.(string)), |
|
Args: []ast.ExprNode{$6.(ast.ExprNode),$4.(ast.ExprNode), direction}, |
|
} |
|
} |
|
| "UPPER" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "WEEKDAY" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "WEEKOFYEAR" '(' Expression ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} |
|
} |
|
| "YEARWEEK" '(' ExpressionList ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: $3.([]ast.ExprNode)} |
|
} |
|
| "CONNECTION_ID" '(' ')' |
|
{ |
|
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string))} |
|
} |
|
|
|
DateArithOpt: |
|
"DATE_ADD" |
|
{ |
|
$$ = ast.DateAdd |
|
} |
|
| "DATE_SUB" |
|
{ |
|
$$ = ast.DateSub |
|
} |
|
|
|
DateArithMultiFormsOpt: |
|
"ADDDATE" |
|
{ |
|
$$ = ast.DateAdd |
|
} |
|
| "SUBDATE" |
|
{ |
|
$$ = ast.DateSub |
|
} |
|
|
|
DateArithInterval: |
|
Expression |
|
{ |
|
$$ = ast.DateArithInterval{ |
|
Unit: "day", |
|
Interval: $1.(ast.ExprNode), |
|
} |
|
} |
|
| "INTERVAL" Expression TimeUnit |
|
{ |
|
$$ = ast.DateArithInterval{Unit: $3.(string), Interval: $2.(ast.ExprNode)} |
|
} |
|
|
|
TrimDirection: |
|
"BOTH" |
|
{ |
|
$$ = ast.TrimBoth |
|
} |
|
| "LEADING" |
|
{ |
|
$$ = ast.TrimLeading |
|
} |
|
| "TRAILING" |
|
{ |
|
$$ = ast.TrimTrailing |
|
} |
|
|
|
FunctionCallAgg: |
|
"AVG" '(' DistinctOpt ExpressionList ')' |
|
{ |
|
$$ = &ast.AggregateFuncExpr{F: $1.(string), Args: $4.([]ast.ExprNode), Distinct: $3.(bool)} |
|
} |
|
| "COUNT" '(' DistinctOpt ExpressionList ')' |
|
{ |
|
$$ = &ast.AggregateFuncExpr{F: $1.(string), Args: $4.([]ast.ExprNode), Distinct: $3.(bool)} |
|
} |
|
| "COUNT" '(' DistinctOpt '*' ')' |
|
{ |
|
args := []ast.ExprNode{ast.NewValueExpr(ast.UnquoteString("*"))} |
|
$$ = &ast.AggregateFuncExpr{F: $1.(string), Args: args, Distinct: $3.(bool)} |
|
} |
|
| "GROUP_CONCAT" '(' DistinctOpt ExpressionList ')' |
|
{ |
|
$$ = &ast.AggregateFuncExpr{F: $1.(string), Args: $4.([]ast.ExprNode), Distinct: $3.(bool)} |
|
} |
|
| "MAX" '(' DistinctOpt Expression ')' |
|
{ |
|
$$ = &ast.AggregateFuncExpr{F: $1.(string), Args: []ast.ExprNode{$4.(ast.ExprNode)}, Distinct: $3.(bool)} |
|
} |
|
| "MIN" '(' DistinctOpt Expression ')' |
|
{ |
|
$$ = &ast.AggregateFuncExpr{F: $1.(string), Args: []ast.ExprNode{$4.(ast.ExprNode)}, Distinct: $3.(bool)} |
|
} |
|
| "SUM" '(' DistinctOpt Expression ')' |
|
{ |
|
$$ = &ast.AggregateFuncExpr{F: $1.(string), Args: []ast.ExprNode{$4.(ast.ExprNode)}, Distinct: $3.(bool)} |
|
} |
|
|
|
FuncDatetimePrec: |
|
{ |
|
$$ = nil |
|
} |
|
| '(' ')' |
|
{ |
|
$$ = nil |
|
} |
|
| '(' Expression ')' |
|
{ |
|
$$ = $2 |
|
} |
|
|
|
TimeUnit: |
|
"MICROSECOND" | "SECOND" | "MINUTE" | "HOUR" | "DAY" | "WEEK" |
|
| "MONTH" | "QUARTER" | "YEAR" | "SECOND_MICROSECOND" | "MINUTE_MICROSECOND" |
|
| "MINUTE_SECOND" | "HOUR_MICROSECOND" | "HOUR_SECOND" | "HOUR_MINUTE" |
|
| "DAY_MICROSECOND" | "DAY_SECOND" | "DAY_MINUTE" | "DAY_HOUR" | "YEAR_MONTH" |
|
|
|
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.(ast.ExprNode), |
|
Result: $4.(ast.ExprNode), |
|
} |
|
} |
|
|
|
ElseOpt: |
|
/* empty */ |
|
{ |
|
$$ = nil |
|
} |
|
| "ELSE" Expression |
|
{ |
|
$$ = $2 |
|
} |
|
|
|
CastType: |
|
"BINARY" OptFieldLen |
|
{ |
|
x := types.NewFieldType(mysql.TypeString) |
|
x.Flen = $2.(int) |
|
x.Charset = charset.CharsetBin |
|
x.Collate = charset.CharsetBin |
|
$$ = x |
|
} |
|
| "CHAR" OptFieldLen OptBinary OptCharset |
|
{ |
|
x := types.NewFieldType(mysql.TypeString) |
|
x.Flen = $2.(int) |
|
if $3.(bool) { |
|
x.Flag |= mysql.BinaryFlag |
|
} |
|
x.Charset = $4.(string) |
|
$$ = x |
|
} |
|
| "DATE" |
|
{ |
|
x := types.NewFieldType(mysql.TypeDate) |
|
$$ = x |
|
} |
|
| "DATETIME" OptFieldLen |
|
{ |
|
x := types.NewFieldType(mysql.TypeDatetime) |
|
x.Decimal = $2.(int) |
|
$$ = x |
|
} |
|
| "DECIMAL" FloatOpt |
|
{ |
|
fopt := $2.(*ast.FloatOpt) |
|
x := types.NewFieldType(mysql.TypeNewDecimal) |
|
x.Flen = fopt.Flen |
|
x.Decimal = fopt.Decimal |
|
$$ = x |
|
} |
|
| "TIME" OptFieldLen |
|
{ |
|
x := types.NewFieldType(mysql.TypeDuration) |
|
x.Decimal = $2.(int) |
|
$$ = x |
|
} |
|
| "SIGNED" OptInteger |
|
{ |
|
x := types.NewFieldType(mysql.TypeLonglong) |
|
$$ = x |
|
} |
|
| "UNSIGNED" OptInteger |
|
{ |
|
x := types.NewFieldType(mysql.TypeLonglong) |
|
x.Flag |= mysql.UnsignedFlag |
|
$$ = x |
|
} |
|
|
|
|
|
PrimaryFactor: |
|
PrimaryFactor '|' PrimaryFactor %prec '|' |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.Or, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryFactor '&' PrimaryFactor %prec '&' |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.And, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryFactor "<<" PrimaryFactor %prec lsh |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.LeftShift, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryFactor ">>" PrimaryFactor %prec rsh |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.RightShift, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryFactor '+' PrimaryFactor %prec '+' |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.Plus, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryFactor '-' PrimaryFactor %prec '-' |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.Minus, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryFactor '*' PrimaryFactor %prec '*' |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.Mul, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryFactor '/' PrimaryFactor %prec '/' |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.Div, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryFactor '%' PrimaryFactor %prec '%' |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.Mod, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryFactor "DIV" PrimaryFactor %prec div |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.IntDiv, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryFactor "MOD" PrimaryFactor %prec mod |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.Mod, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryFactor '^' PrimaryFactor |
|
{ |
|
$$ = &ast.BinaryOperationExpr{Op: opcode.Xor, L: $1.(ast.ExprNode), R: $3.(ast.ExprNode)} |
|
} |
|
| PrimaryExpression |
|
|
|
|
|
Priority: |
|
{ |
|
$$ = ast.NoPriority |
|
} |
|
| "LOW_PRIORITY" |
|
{ |
|
$$ = ast.LowPriority |
|
} |
|
| "HIGH_PRIORITY" |
|
{ |
|
$$ = ast.HighPriority |
|
} |
|
| "DELAYED" |
|
{ |
|
$$ = ast.DelayedPriority |
|
} |
|
|
|
LowPriorityOptional: |
|
{ |
|
$$ = false |
|
} |
|
| "LOW_PRIORITY" |
|
{ |
|
$$ = true |
|
} |
|
|
|
TableName: |
|
Identifier |
|
{ |
|
$$ = &ast.TableName{Name:model.NewCIStr($1.(string))} |
|
} |
|
| Identifier '.' Identifier |
|
{ |
|
$$ = &ast.TableName{Schema:model.NewCIStr($1.(string)), Name:model.NewCIStr($3.(string))} |
|
} |
|
|
|
TableNameList: |
|
TableName |
|
{ |
|
tbl := []*ast.TableName{$1.(*ast.TableName)} |
|
$$ = tbl |
|
} |
|
| TableNameList ',' TableName |
|
{ |
|
$$ = append($1.([]*ast.TableName), $3.(*ast.TableName)) |
|
} |
|
|
|
QuickOptional: |
|
%prec lowerThanQuick |
|
{ |
|
$$ = 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.(string), |
|
SQLText: sqlText, |
|
SQLVar: sqlVar, |
|
} |
|
} |
|
|
|
PrepareSQL: |
|
stringLit |
|
| UserVariable |
|
|
|
|
|
/* |
|
* 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.(string)} |
|
} |
|
| "EXECUTE" Identifier "USING" UserVariableList |
|
{ |
|
$$ = &ast.ExecuteStmt{ |
|
Name: $2.(string), |
|
UsingVars: $4.([]ast.ExprNode), |
|
} |
|
} |
|
|
|
UserVariableList: |
|
UserVariable |
|
{ |
|
$$ = []ast.ExprNode{$1.(ast.ExprNode)} |
|
} |
|
| UserVariableList ',' UserVariable |
|
{ |
|
$$ = append($1.([]ast.ExprNode), $3.(ast.ExprNode)) |
|
} |
|
|
|
/* |
|
* See: https://dev.mysql.com/doc/refman/5.0/en/deallocate-prepare.html |
|
*/ |
|
|
|
DeallocateStmt: |
|
DeallocateSym "PREPARE" Identifier |
|
{ |
|
$$ = &ast.DeallocateStmt{Name: $3.(string)} |
|
} |
|
|
|
DeallocateSym: |
|
"DEALLOCATE" | "DROP" |
|
|
|
/****************************Prepared Statement End*******************************/ |
|
|
|
|
|
RollbackStmt: |
|
"ROLLBACK" |
|
{ |
|
$$ = &ast.RollbackStmt{} |
|
} |
|
|
|
SelectStmt: |
|
"SELECT" SelectStmtOpts SelectStmtFieldList SelectStmtLimit SelectLockOpt |
|
{ |
|
st := &ast.SelectStmt { |
|
Distinct: $2.(bool), |
|
Fields: $3.(*ast.FieldList), |
|
LockTp: $5.(ast.SelectLockType), |
|
} |
|
lastField := st.Fields.Fields[len(st.Fields.Fields)-1] |
|
if lastField.Expr != nil && lastField.AsName.O == "" { |
|
src := yylex.(*lexer).src |
|
var lastEnd int |
|
if $4 != nil { |
|
lastEnd = yyS[yypt-1].offset-1 |
|
} else if $5 != ast.SelectLockNone { |
|
lastEnd = yyS[yypt].offset-1 |
|
} else { |
|
lastEnd = len(src) |
|
if src[lastEnd-1] == ';' { |
|
lastEnd-- |
|
} |
|
} |
|
lastField.SetText(src[lastField.Offset:lastEnd]) |
|
} |
|
if $4 != nil { |
|
st.Limit = $4.(*ast.Limit) |
|
} |
|
$$ = st |
|
} |
|
| "SELECT" SelectStmtOpts SelectStmtFieldList FromDual WhereClauseOptional SelectStmtLimit SelectLockOpt |
|
{ |
|
st := &ast.SelectStmt { |
|
Distinct: $2.(bool), |
|
Fields: $3.(*ast.FieldList), |
|
LockTp: $7.(ast.SelectLockType), |
|
} |
|
lastField := st.Fields.Fields[len(st.Fields.Fields)-1] |
|
if lastField.Expr != nil && lastField.AsName.O == "" { |
|
lastEnd := yyS[yypt-3].offset-1 |
|
lastField.SetText(yylex.(*lexer).src[lastField.Offset:lastEnd]) |
|
} |
|
if $5 != nil { |
|
st.Where = $5.(ast.ExprNode) |
|
} |
|
if $6 != nil { |
|
st.Limit = $6.(*ast.Limit) |
|
} |
|
$$ = st |
|
} |
|
| "SELECT" SelectStmtOpts SelectStmtFieldList "FROM" |
|
TableRefsClause WhereClauseOptional SelectStmtGroup HavingClause OrderByOptional |
|
SelectStmtLimit SelectLockOpt |
|
{ |
|
st := &ast.SelectStmt{ |
|
Distinct: $2.(bool), |
|
Fields: $3.(*ast.FieldList), |
|
From: $5.(*ast.TableRefsClause), |
|
LockTp: $11.(ast.SelectLockType), |
|
} |
|
|
|
lastField := st.Fields.Fields[len(st.Fields.Fields)-1] |
|
if lastField.Expr != nil && lastField.AsName.O == "" { |
|
lastEnd := yyS[yypt-7].offset-1 |
|
lastField.SetText(yylex.(*lexer).src[lastField.Offset:lastEnd]) |
|
} |
|
|
|
if $6 != nil { |
|
st.Where = $6.(ast.ExprNode) |
|
} |
|
|
|
if $7 != nil { |
|
st.GroupBy = $7.(*ast.GroupByClause) |
|
} |
|
|
|
if $8 != nil { |
|
st.Having = $8.(*ast.HavingClause) |
|
} |
|
|
|
if $9 != nil { |
|
st.OrderBy = $9.(*ast.OrderByClause) |
|
} |
|
|
|
if $10 != nil { |
|
st.Limit = $10.(*ast.Limit) |
|
} |
|
|
|
$$ = st |
|
} |
|
|
|
FromDual: |
|
"FROM" "DUAL" |
|
|
|
|
|
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 TableAsNameOpt |
|
{ |
|
$$ = &ast.TableSource{Source: $1.(*ast.TableName), AsName: $2.(model.CIStr)} |
|
} |
|
| '(' SelectStmt ')' TableAsName |
|
{ |
|
st := $2.(*ast.SelectStmt) |
|
l := yylex.(*lexer) |
|
endOffset := l.endOffset(yyS[yypt-1].offset) |
|
l.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 |
|
} |
|
|
|
TableAsNameOpt: |
|
{ |
|
$$ = model.CIStr{} |
|
} |
|
| TableAsName |
|
{ |
|
$$ = $1 |
|
} |
|
|
|
TableAsName: |
|
Identifier |
|
{ |
|
$$ = model.NewCIStr($1.(string)) |
|
} |
|
| "AS" Identifier |
|
{ |
|
$$ = model.NewCIStr($2.(string)) |
|
} |
|
|
|
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.ExprNode)} |
|
$$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $3.(ast.ResultSetNode), Tp: ast.CrossJoin, On: on} |
|
} |
|
| TableRef JoinType OuterOpt "JOIN" TableRef "ON" Expression |
|
{ |
|
on := &ast.OnCondition{Expr: $7.(ast.ExprNode)} |
|
$$ = &ast.Join{Left: $1.(ast.ResultSetNode), Right: $5.(ast.ResultSetNode), Tp: $2.(ast.JoinType), On: on} |
|
} |
|
/* Support Using */ |
|
|
|
JoinType: |
|
"LEFT" |
|
{ |
|
$$ = ast.LeftJoin |
|
} |
|
| "RIGHT" |
|
{ |
|
$$ = ast.RightJoin |
|
} |
|
|
|
OuterOpt: |
|
{ |
|
$$ = nil |
|
} |
|
| "OUTER" |
|
|
|
|
|
CrossOpt: |
|
"JOIN" |
|
| "CROSS" "JOIN" |
|
| "INNER" "JOIN" |
|
|
|
|
|
LimitClause: |
|
{ |
|
$$ = nil |
|
} |
|
| "LIMIT" LengthNum |
|
{ |
|
$$ = &ast.Limit{Count: $2.(uint64)} |
|
} |
|
|
|
SelectStmtLimit: |
|
{ |
|
$$ = nil |
|
} |
|
| "LIMIT" LengthNum |
|
{ |
|
$$ = &ast.Limit{Count: $2.(uint64)} |
|
} |
|
| "LIMIT" LengthNum ',' LengthNum |
|
{ |
|
$$ = &ast.Limit{Offset: $2.(uint64), Count: $4.(uint64)} |
|
} |
|
| "LIMIT" LengthNum "OFFSET" LengthNum |
|
{ |
|
$$ = &ast.Limit{Offset: $4.(uint64), Count: $2.(uint64)} |
|
} |
|
|
|
SelectStmtDistinct: |
|
/* EMPTY */ |
|
{ |
|
$$ = false |
|
} |
|
| "ALL" |
|
{ |
|
$$ = false |
|
} |
|
| "DISTINCT" |
|
{ |
|
$$ = true |
|
} |
|
|
|
SelectStmtOpts: |
|
SelectStmtDistinct SelectStmtCalcFoundRows |
|
{ |
|
// TODO: return calc_found_rows opt and support more other options |
|
$$ = $1 |
|
} |
|
|
|
SelectStmtCalcFoundRows: |
|
%prec lowerThanCalcFoundRows |
|
{ |
|
$$ = false |
|
} |
|
| "SQL_CALC_FOUND_ROWS" |
|
{ |
|
$$ = 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) |
|
l := yylex.(*lexer) |
|
endOffset := l.endOffset(yyS[yypt].offset) |
|
l.SetLastSelectFieldText(s, endOffset) |
|
src := yylex.(*lexer).src |
|
// See the implemention of yyParse function |
|
s.SetText(src[yyS[yypt-1].offset-1:yyS[yypt].offset-1]) |
|
$$ = &ast.SubqueryExpr{Query: s} |
|
} |
|
| '(' UnionStmt ')' |
|
{ |
|
s := $2.(*ast.UnionStmt) |
|
src := yylex.(*lexer).src |
|
// See the implemention of yyParse function |
|
s.SetText(src[yyS[yypt-1].offset-1:yyS[yypt].offset-1]) |
|
$$ = &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 |
|
} |
|
| "LOCK" "IN" "SHARE" "MODE" |
|
{ |
|
$$ = ast.SelectLockInShareMode |
|
} |
|
|
|
// See: https://dev.mysql.com/doc/refman/5.7/en/union.html |
|
UnionStmt: |
|
UnionClauseList "UNION" UnionOpt SelectStmt |
|
{ |
|
union := $1.(*ast.UnionStmt) |
|
union.Distinct = union.Distinct || $3.(bool) |
|
lastSelect := union.SelectList.Selects[len(union.SelectList.Selects)-1] |
|
l := yylex.(*lexer) |
|
endOffset := l.endOffset(yyS[yypt-2].offset) |
|
l.SetLastSelectFieldText(lastSelect, endOffset) |
|
union.SelectList.Selects = append(union.SelectList.Selects, $4.(*ast.SelectStmt)) |
|
$$ = union |
|
} |
|
| UnionClauseList "UNION" UnionOpt '(' SelectStmt ')' OrderByOptional SelectStmtLimit |
|
{ |
|
union := $1.(*ast.UnionStmt) |
|
union.Distinct = union.Distinct || $3.(bool) |
|
lastSelect := union.SelectList.Selects[len(union.SelectList.Selects)-1] |
|
l := yylex.(*lexer) |
|
endOffset := l.endOffset(yyS[yypt-6].offset) |
|
l.SetLastSelectFieldText(lastSelect, endOffset) |
|
st := $5.(*ast.SelectStmt) |
|
endOffset = l.endOffset(yyS[yypt-2].offset) |
|
l.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) |
|
union.Distinct = union.Distinct || $3.(bool) |
|
lastSelect := union.SelectList.Selects[len(union.SelectList.Selects)-1] |
|
l := yylex.(*lexer) |
|
endOffset := l.endOffset(yyS[yypt-2].offset) |
|
l.SetLastSelectFieldText(lastSelect, endOffset) |
|
union.SelectList.Selects = append(union.SelectList.Selects, $4.(*ast.SelectStmt)) |
|
$$ = union |
|
} |
|
|
|
UnionSelect: |
|
SelectStmt |
|
| '(' SelectStmt ')' |
|
{ |
|
st := $2.(*ast.SelectStmt) |
|
l := yylex.(*lexer) |
|
endOffset := l.endOffset(yyS[yypt].offset) |
|
l.SetLastSelectFieldText(st, endOffset) |
|
$$ = st |
|
} |
|
|
|
UnionOpt: |
|
{ |
|
$$ = true |
|
} |
|
| "ALL" |
|
{ |
|
$$ = false |
|
} |
|
| "DISTINCT" |
|
{ |
|
$$ = true |
|
} |
|
|
|
|
|
/********************Set Statement*******************************/ |
|
SetStmt: |
|
"SET" VariableAssignmentList |
|
{ |
|
$$ = &ast.SetStmt{Variables: $2.([]*ast.VariableAssignment)} |
|
} |
|
| "SET" "NAMES" StringName |
|
{ |
|
$$ = &ast.SetCharsetStmt{Charset: $3.(string)} |
|
} |
|
| "SET" "NAMES" StringName "COLLATE" StringName |
|
{ |
|
$$ = &ast.SetCharsetStmt{ |
|
Charset: $3.(string), |
|
Collate: $5.(string), |
|
} |
|
} |
|
| "SET" CharsetKw StringName |
|
{ |
|
$$ = &ast.SetCharsetStmt{Charset: $3.(string)} |
|
} |
|
| "SET" "PASSWORD" eq PasswordOpt |
|
{ |
|
$$ = &ast.SetPwdStmt{Password: $4.(string)} |
|
} |
|
| "SET" "PASSWORD" "FOR" Username eq PasswordOpt |
|
{ |
|
$$ = &ast.SetPwdStmt{User: $4.(string), Password: $6.(string)} |
|
} |
|
| "SET" "GLOBAL" "TRANSACTION" TransactionChars |
|
{ |
|
// Parsed but ignored |
|
} |
|
| "SET" "SESSION" "TRANSACTION" TransactionChars |
|
{ |
|
// Parsed but ignored |
|
} |
|
|
|
TransactionChars: |
|
TransactionChar |
|
| TransactionChars ',' TransactionChar |
|
|
|
TransactionChar: |
|
"ISOLATION" "LEVEL" IsolationLevel |
|
| "READ" "WRITE" |
|
| "READ" "ONLY" |
|
|
|
IsolationLevel: |
|
"REPEATABLE" "READ" |
|
| "READ" "COMMITTED" |
|
| "READ" "UNCOMMITTED" |
|
| "SERIALIZABLE" |
|
|
|
VariableAssignment: |
|
Identifier eq Expression |
|
{ |
|
$$ = &ast.VariableAssignment{Name: $1.(string), Value: $3.(ast.ExprNode), IsSystem: true} |
|
} |
|
| "GLOBAL" Identifier eq Expression |
|
{ |
|
$$ = &ast.VariableAssignment{Name: $2.(string), Value: $4.(ast.ExprNode), IsGlobal: true, IsSystem: true} |
|
} |
|
| "SESSION" Identifier eq Expression |
|
{ |
|
$$ = &ast.VariableAssignment{Name: $2.(string), Value: $4.(ast.ExprNode), IsSystem: true} |
|
} |
|
| "LOCAL" Identifier eq Expression |
|
{ |
|
$$ = &ast.VariableAssignment{Name: $2.(string), Value: $4.(ast.ExprNode), IsSystem: true} |
|
} |
|
| "SYS_VAR" eq Expression |
|
{ |
|
v := strings.ToLower($1.(string)) |
|
var isGlobal bool |
|
if strings.HasPrefix(v, "@@global.") { |
|
isGlobal = true |
|
v = strings.TrimPrefix(v, "@@global.") |
|
} else if strings.HasPrefix(v, "@@session.") { |
|
v = strings.TrimPrefix(v, "@@session.") |
|
} else if strings.HasPrefix(v, "@@local.") { |
|
v = strings.TrimPrefix(v, "@@local.") |
|
} else if strings.HasPrefix(v, "@@") { |
|
v = strings.TrimPrefix(v, "@@") |
|
} |
|
$$ = &ast.VariableAssignment{Name: v, Value: $3.(ast.ExprNode), IsGlobal: isGlobal, IsSystem: true} |
|
} |
|
| "USER_VAR" eq Expression |
|
{ |
|
v := $1.(string) |
|
v = strings.TrimPrefix(v, "@") |
|
$$ = &ast.VariableAssignment{Name: v, Value: $3.(ast.ExprNode)} |
|
} |
|
|
|
VariableAssignmentList: |
|
{ |
|
$$ = []*ast.VariableAssignment{} |
|
} |
|
| VariableAssignment |
|
{ |
|
$$ = []*ast.VariableAssignment{$1.(*ast.VariableAssignment)} |
|
} |
|
| VariableAssignmentList ',' VariableAssignment |
|
{ |
|
$$ = append($1.([]*ast.VariableAssignment), $3.(*ast.VariableAssignment)) |
|
} |
|
|
|
Variable: |
|
SystemVariable | UserVariable |
|
|
|
SystemVariable: |
|
"SYS_VAR" |
|
{ |
|
v := strings.ToLower($1.(string)) |
|
var isGlobal bool |
|
if strings.HasPrefix(v, "@@global.") { |
|
isGlobal = true |
|
v = strings.TrimPrefix(v, "@@global.") |
|
} else if strings.HasPrefix(v, "@@session.") { |
|
v = strings.TrimPrefix(v, "@@session.") |
|
} else if strings.HasPrefix(v, "@@local.") { |
|
v = strings.TrimPrefix(v, "@@local.") |
|
} else if strings.HasPrefix(v, "@@") { |
|
v = strings.TrimPrefix(v, "@@") |
|
} |
|
$$ = &ast.VariableExpr{Name: v, IsGlobal: isGlobal, IsSystem: true} |
|
} |
|
|
|
UserVariable: |
|
"USER_VAR" |
|
{ |
|
v := $1.(string) |
|
v = strings.TrimPrefix(v, "@") |
|
$$ = &ast.VariableExpr{Name: v, IsGlobal: false, IsSystem: false} |
|
} |
|
|
|
Username: |
|
stringLit "AT" stringLit |
|
{ |
|
$$ = $1.(string) + "@" + $3.(string) |
|
} |
|
|
|
PasswordOpt: |
|
stringLit |
|
{ |
|
$$ = $1.(string) |
|
} |
|
| "PASSWORD" '(' AuthString ')' |
|
{ |
|
$$ = $3.(string) |
|
} |
|
|
|
AuthString: |
|
stringLit |
|
{ |
|
$$ = $1.(string) |
|
} |
|
|
|
/****************************Admin Statement*******************************/ |
|
AdminStmt: |
|
"ADMIN" "SHOW" "DDL" |
|
{ |
|
$$ = &ast.AdminStmt{Tp: ast.AdminShowDDL} |
|
} |
|
| "ADMIN" "CHECK" "TABLE" TableNameList |
|
{ |
|
$$ = &ast.AdminStmt{ |
|
Tp: ast.AdminCheckTable, |
|
Tables: $4.([]*ast.TableName), |
|
} |
|
} |
|
|
|
/****************************Show Statement*******************************/ |
|
ShowStmt: |
|
"SHOW" ShowTargetFilterable ShowLikeOrWhereOpt |
|
{ |
|
stmt := $2.(*ast.ShowStmt) |
|
if $3 != nil { |
|
if x, ok := $3.(*ast.PatternLikeExpr); ok { |
|
stmt.Pattern = x |
|
} else { |
|
stmt.Where = $3.(ast.ExprNode) |
|
} |
|
} |
|
$$ = stmt |
|
} |
|
| "SHOW" "CREATE" "TABLE" TableName |
|
{ |
|
$$ = &ast.ShowStmt{ |
|
Tp: ast.ShowCreateTable, |
|
Table: $4.(*ast.TableName), |
|
} |
|
} |
|
| "SHOW" "GRANTS" |
|
{ |
|
// See: https://dev.mysql.com/doc/refman/5.7/en/show-grants.html |
|
$$ = &ast.ShowStmt{Tp: ast.ShowGrants} |
|
} |
|
| "SHOW" "GRANTS" "FOR" Username |
|
{ |
|
// See: https://dev.mysql.com/doc/refman/5.7/en/show-grants.html |
|
$$ = &ast.ShowStmt{ |
|
Tp: ast.ShowGrants, |
|
User: $4.(string), |
|
} |
|
} |
|
| "SHOW" "INDEX" "FROM" TableName |
|
{ |
|
$$ = &ast.ShowStmt{ |
|
Tp: ast.ShowIndex, |
|
Table: $4.(*ast.TableName), |
|
} |
|
} |
|
|
|
ShowTargetFilterable: |
|
"ENGINES" |
|
{ |
|
$$ = &ast.ShowStmt{Tp: ast.ShowEngines} |
|
} |
|
| "DATABASES" |
|
{ |
|
$$ = &ast.ShowStmt{Tp: ast.ShowDatabases} |
|
} |
|
| "SCHEMAS" |
|
{ |
|
$$ = &ast.ShowStmt{Tp: ast.ShowDatabases} |
|
} |
|
| "CHARACTER" "SET" |
|
{ |
|
$$ = &ast.ShowStmt{Tp: ast.ShowCharset} |
|
} |
|
| OptFull "TABLES" ShowDatabaseNameOpt |
|
{ |
|
$$ = &ast.ShowStmt{ |
|
Tp: ast.ShowTables, |
|
DBName: $3.(string), |
|
Full: $1.(bool), |
|
} |
|
} |
|
| "TABLE" "STATUS" ShowDatabaseNameOpt |
|
{ |
|
$$ = &ast.ShowStmt{ |
|
Tp: ast.ShowTableStatus, |
|
DBName: $3.(string), |
|
} |
|
} |
|
| OptFull "COLUMNS" ShowTableAliasOpt ShowDatabaseNameOpt |
|
{ |
|
$$ = &ast.ShowStmt{ |
|
Tp: ast.ShowColumns, |
|
Table: $3.(*ast.TableName), |
|
DBName: $4.(string), |
|
Full: $1.(bool), |
|
} |
|
} |
|
| OptFull "FIELDS" ShowTableAliasOpt ShowDatabaseNameOpt |
|
{ |
|
// SHOW FIELDS is a synonym for SHOW COLUMNS. |
|
$$ = &ast.ShowStmt{ |
|
Tp: ast.ShowColumns, |
|
Table: $3.(*ast.TableName), |
|
DBName: $4.(string), |
|
Full: $1.(bool), |
|
} |
|
} |
|
| "WARNINGS" |
|
{ |
|
$$ = &ast.ShowStmt{Tp: ast.ShowWarnings} |
|
} |
|
| GlobalScope "VARIABLES" |
|
{ |
|
$$ = &ast.ShowStmt{ |
|
Tp: ast.ShowVariables, |
|
GlobalScope: $1.(bool), |
|
} |
|
} |
|
| GlobalScope "STATUS" |
|
{ |
|
$$ = &ast.ShowStmt{ |
|
Tp: ast.ShowStatus, |
|
GlobalScope: $1.(bool), |
|
} |
|
} |
|
| "COLLATION" |
|
{ |
|
$$ = &ast.ShowStmt{ |
|
Tp: ast.ShowCollation, |
|
} |
|
} |
|
| "TRIGGERS" ShowDatabaseNameOpt |
|
{ |
|
$$ = &ast.ShowStmt{ |
|
Tp: ast.ShowTriggers, |
|
DBName: $2.(string), |
|
} |
|
} |
|
| "PROCEDURE" "STATUS" |
|
{ |
|
$$ = &ast.ShowStmt { |
|
Tp: ast.ShowProcedureStatus, |
|
} |
|
} |
|
|
|
ShowLikeOrWhereOpt: |
|
{ |
|
$$ = nil |
|
} |
|
| "LIKE" PrimaryExpression |
|
{ |
|
$$ = &ast.PatternLikeExpr{Pattern: $2.(ast.ExprNode)} |
|
} |
|
| "WHERE" Expression |
|
{ |
|
$$ = $2.(ast.ExprNode) |
|
} |
|
|
|
GlobalScope: |
|
{ |
|
$$ = false |
|
} |
|
| "GLOBAL" |
|
{ |
|
$$ = true |
|
} |
|
| "SESSION" |
|
{ |
|
$$ = false |
|
} |
|
|
|
OptFull: |
|
{ |
|
$$ = false |
|
} |
|
| "FULL" |
|
{ |
|
$$ = true |
|
} |
|
|
|
ShowDatabaseNameOpt: |
|
{ |
|
$$ = "" |
|
} |
|
| "FROM" DBName |
|
{ |
|
$$ = $2.(string) |
|
} |
|
| "IN" DBName |
|
{ |
|
$$ = $2.(string) |
|
} |
|
|
|
ShowTableAliasOpt: |
|
"FROM" TableName |
|
{ |
|
$$ = $2.(*ast.TableName) |
|
} |
|
| "IN" TableName |
|
{ |
|
$$ = $2.(*ast.TableName) |
|
} |
|
|
|
Statement: |
|
EmptyStmt |
|
| AdminStmt |
|
| AlterTableStmt |
|
| BeginTransactionStmt |
|
| CommitStmt |
|
| DeallocateStmt |
|
| DeleteFromStmt |
|
| ExecuteStmt |
|
| ExplainStmt |
|
| CreateDatabaseStmt |
|
| CreateIndexStmt |
|
| CreateTableStmt |
|
| CreateUserStmt |
|
| DoStmt |
|
| DropDatabaseStmt |
|
| DropIndexStmt |
|
| DropTableStmt |
|
| GrantStmt |
|
| InsertIntoStmt |
|
| PreparedStmt |
|
| RollbackStmt |
|
| ReplaceIntoStmt |
|
| SelectStmt |
|
| UnionStmt |
|
| SetStmt |
|
| ShowStmt |
|
| TruncateTableStmt |
|
| UpdateStmt |
|
| UseStmt |
|
| SubSelect |
|
{ |
|
// `(select 1)`; is a valid select statement |
|
// TODO: This is used to fix issue #320. There may be a better solution. |
|
$$ = $1.(*ast.SubqueryExpr).Query |
|
} |
|
| UnlockTablesStmt |
|
| LockTablesStmt |
|
|
|
ExplainableStmt: |
|
SelectStmt |
|
| DeleteFromStmt |
|
| UpdateStmt |
|
| InsertIntoStmt |
|
| ReplaceIntoStmt |
|
|
|
StatementList: |
|
Statement |
|
{ |
|
if $1 != nil { |
|
s := $1.(ast.StmtNode) |
|
s.SetText(yylex.(*lexer).stmtText()) |
|
yylex.(*lexer).list = append(yylex.(*lexer).list, s) |
|
} |
|
} |
|
| StatementList ';' Statement |
|
{ |
|
if $3 != nil { |
|
s := $3.(ast.StmtNode) |
|
s.SetText(yylex.(*lexer).stmtText()) |
|
yylex.(*lexer).list = append(yylex.(*lexer).list, s) |
|
} |
|
} |
|
|
|
Constraint: |
|
ConstraintKeywordOpt ConstraintElem |
|
{ |
|
cst := $2.(*ast.Constraint) |
|
if $1 != nil { |
|
cst.Name = $1.(string) |
|
} |
|
$$ = cst |
|
} |
|
|
|
TableElement: |
|
ColumnDef |
|
{ |
|
$$ = $1.(*ast.ColumnDef) |
|
} |
|
| Constraint |
|
{ |
|
$$ = $1.(*ast.Constraint) |
|
} |
|
| "CHECK" '(' Expression ')' |
|
{ |
|
/* Nothing to do now */ |
|
$$ = nil |
|
} |
|
|
|
TableElementList: |
|
TableElement |
|
{ |
|
if $1 != nil { |
|
$$ = []interface{}{$1.(interface{})} |
|
} else { |
|
$$ = []interface{}{} |
|
} |
|
} |
|
| TableElementList ',' TableElement |
|
{ |
|
if $3 != nil { |
|
$$ = append($1.([]interface{}), $3) |
|
} else { |
|
$$ = $1 |
|
} |
|
} |
|
|
|
TableOption: |
|
"ENGINE" Identifier |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionEngine, StrValue: $2.(string)} |
|
} |
|
| "ENGINE" eq Identifier |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionEngine, StrValue: $3.(string)} |
|
} |
|
| DefaultKwdOpt CharsetKw EqOpt StringName |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionCharset, StrValue: $4.(string)} |
|
} |
|
| DefaultKwdOpt "COLLATE" EqOpt StringName |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionCollate, StrValue: $4.(string)} |
|
} |
|
| "AUTO_INCREMENT" eq LengthNum |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionAutoIncrement, UintValue: $3.(uint64)} |
|
} |
|
| "COMMENT" EqOpt stringLit |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionComment, StrValue: $3.(string)} |
|
} |
|
| "AVG_ROW_LENGTH" EqOpt LengthNum |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionAvgRowLength, UintValue: $3.(uint64)} |
|
} |
|
| "CONNECTION" EqOpt stringLit |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionConnection, StrValue: $3.(string)} |
|
} |
|
| "CHECKSUM" EqOpt LengthNum |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionCheckSum, UintValue: $3.(uint64)} |
|
} |
|
| "PASSWORD" EqOpt stringLit |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionPassword, StrValue: $3.(string)} |
|
} |
|
| "COMPRESSION" EqOpt Identifier |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionCompression, StrValue: $3.(string)} |
|
} |
|
| "KEY_BLOCK_SIZE" EqOpt LengthNum |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionKeyBlockSize, UintValue: $3.(uint64)} |
|
} |
|
| "MAX_ROWS" EqOpt LengthNum |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionMaxRows, UintValue: $3.(uint64)} |
|
} |
|
| "MIN_ROWS" EqOpt LengthNum |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionMinRows, UintValue: $3.(uint64)} |
|
} |
|
| "DELAY_KEY_WRITE" EqOpt LengthNum |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionDelayKeyWrite, UintValue: $3.(uint64)} |
|
} |
|
| RowFormat |
|
{ |
|
$$ = &ast.TableOption{Tp: ast.TableOptionRowFormat, UintValue: $1.(uint64)} |
|
} |
|
|
|
|
|
TableOptionListOpt: |
|
{ |
|
$$ = []*ast.TableOption{} |
|
} |
|
| TableOptionList %prec lowerThanComma |
|
|
|
TableOptionList: |
|
TableOption |
|
{ |
|
$$ = []*ast.TableOption{$1.(*ast.TableOption)} |
|
} |
|
| TableOptionList TableOption |
|
{ |
|
$$ = append($1.([]*ast.TableOption), $2.(*ast.TableOption)) |
|
} |
|
| TableOptionList ',' TableOption |
|
{ |
|
$$ = append($1.([]*ast.TableOption), $3.(*ast.TableOption)) |
|
} |
|
|
|
|
|
TruncateTableStmt: |
|
"TRUNCATE" "TABLE" TableName |
|
{ |
|
$$ = &ast.TruncateTableStmt{Table: $3.(*ast.TableName)} |
|
} |
|
|
|
RowFormat: |
|
"ROW_FORMAT" EqOpt "DEFAULT" |
|
{ |
|
$$ = ast.RowFormatDefault |
|
} |
|
| "ROW_FORMAT" EqOpt "DYNAMIC" |
|
{ |
|
$$ = ast.RowFormatDynamic |
|
} |
|
| "ROW_FORMAT" EqOpt "FIXED" |
|
{ |
|
$$ = ast.RowFormatFixed |
|
} |
|
| "ROW_FORMAT" EqOpt "COMPRESSED" |
|
{ |
|
$$ = ast.RowFormatCompressed |
|
} |
|
| "ROW_FORMAT" EqOpt "REDUNDANT" |
|
{ |
|
$$ = ast.RowFormatRedundant |
|
} |
|
| "ROW_FORMAT" EqOpt "COMPACT" |
|
{ |
|
$$ = ast.RowFormatCompact |
|
} |
|
|
|
/*************************************Type Begin***************************************/ |
|
Type: |
|
NumericType |
|
{ |
|
$$ = $1 |
|
} |
|
| StringType |
|
{ |
|
$$ = $1 |
|
} |
|
| DateAndTimeType |
|
{ |
|
$$ = $1 |
|
} |
|
| "float32" |
|
{ |
|
x := types.NewFieldType($1.(byte)) |
|
$$ = x |
|
} |
|
| "float64" |
|
{ |
|
x := types.NewFieldType($1.(byte)) |
|
$$ = x |
|
} |
|
| "int64" |
|
{ |
|
x := types.NewFieldType($1.(byte)) |
|
$$ = x |
|
} |
|
| "string" |
|
{ |
|
x := types.NewFieldType($1.(byte)) |
|
$$ = x |
|
} |
|
| "uint" |
|
{ |
|
x := types.NewFieldType($1.(byte)) |
|
$$ = x |
|
} |
|
| "uint64" |
|
{ |
|
x := types.NewFieldType($1.(byte)) |
|
$$ = x |
|
} |
|
|
|
NumericType: |
|
IntegerType OptFieldLen FieldOpts |
|
{ |
|
// TODO: check flen 0 |
|
x := types.NewFieldType($1.(byte)) |
|
x.Flen = $2.(int) |
|
for _, o := range $3.([]*ast.TypeOpt) { |
|
if o.IsUnsigned { |
|
x.Flag |= mysql.UnsignedFlag |
|
} |
|
if o.IsZerofill { |
|
x.Flag |= mysql.ZerofillFlag |
|
} |
|
} |
|
$$ = x |
|
} |
|
| FixedPointType FloatOpt FieldOpts |
|
{ |
|
fopt := $2.(*ast.FloatOpt) |
|
x := types.NewFieldType($1.(byte)) |
|
x.Flen = fopt.Flen |
|
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 |
|
} |
|
| FloatingPointType FloatOpt FieldOpts |
|
{ |
|
fopt := $2.(*ast.FloatOpt) |
|
x := types.NewFieldType($1.(byte)) |
|
x.Flen = fopt.Flen |
|
if x.Tp == mysql.TypeFloat { |
|
// Fix issue #312 |
|
if x.Flen > 53 { |
|
yylex.(*lexer).errf("Float len(%d) should not be greater than 53", x.Flen) |
|
return 1 |
|
} |
|
if x.Flen > 24 { |
|
x.Tp = mysql.TypeDouble |
|
} |
|
} |
|
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 == -1 || x.Flen == 0 { |
|
x.Flen = 1 |
|
} else if x.Flen > 64 { |
|
yylex.(*lexer).errf("invalid field length %d for bit type, must in [1, 64]", x.Flen) |
|
} |
|
$$ = x |
|
} |
|
|
|
IntegerType: |
|
"TINYINT" |
|
{ |
|
$$ = mysql.TypeTiny |
|
} |
|
| "SMALLINT" |
|
{ |
|
$$ = mysql.TypeShort |
|
} |
|
| "MEDIUMINT" |
|
{ |
|
$$ = mysql.TypeInt24 |
|
} |
|
| "INT" |
|
{ |
|
$$ = mysql.TypeLong |
|
} |
|
| "INTEGER" |
|
{ |
|
$$ = mysql.TypeLong |
|
} |
|
| "BIGINT" |
|
{ |
|
$$ = mysql.TypeLonglong |
|
} |
|
| "BOOL" |
|
{ |
|
$$ = mysql.TypeTiny |
|
} |
|
| "BOOLEAN" |
|
{ |
|
$$ = mysql.TypeTiny |
|
} |
|
|
|
OptInteger: |
|
{} | "INTEGER" |
|
|
|
FixedPointType: |
|
"DECIMAL" |
|
{ |
|
$$ = mysql.TypeNewDecimal |
|
} |
|
| "NUMERIC" |
|
{ |
|
$$ = mysql.TypeNewDecimal |
|
} |
|
|
|
FloatingPointType: |
|
"float" |
|
{ |
|
$$ = mysql.TypeFloat |
|
} |
|
| "REAL" |
|
{ |
|
$$ = mysql.TypeDouble |
|
} |
|
| "DOUBLE" |
|
{ |
|
$$ = mysql.TypeDouble |
|
} |
|
| "DOUBLE" "PRECISION" |
|
{ |
|
$$ = mysql.TypeDouble |
|
} |
|
|
|
BitValueType: |
|
"BIT" |
|
{ |
|
$$ = mysql.TypeBit |
|
} |
|
|
|
StringType: |
|
NationalOpt "CHAR" FieldLen OptBinary OptCharset OptCollate |
|
{ |
|
x := types.NewFieldType(mysql.TypeString) |
|
x.Flen = $3.(int) |
|
if $4.(bool) { |
|
x.Flag |= mysql.BinaryFlag |
|
} |
|
$$ = x |
|
} |
|
| NationalOpt "CHAR" OptBinary OptCharset OptCollate |
|
{ |
|
x := types.NewFieldType(mysql.TypeString) |
|
if $3.(bool) { |
|
x.Flag |= mysql.BinaryFlag |
|
} |
|
$$ = x |
|
} |
|
| NationalOpt "VARCHAR" FieldLen OptBinary OptCharset OptCollate |
|
{ |
|
x := types.NewFieldType(mysql.TypeVarchar) |
|
x.Flen = $3.(int) |
|
if $4.(bool) { |
|
x.Flag |= mysql.BinaryFlag |
|
} |
|
x.Charset = $5.(string) |
|
x.Collate = $6.(string) |
|
$$ = x |
|
} |
|
| "BINARY" OptFieldLen |
|
{ |
|
x := types.NewFieldType(mysql.TypeString) |
|
x.Flen = $2.(int) |
|
x.Charset = charset.CharsetBin |
|
x.Collate = charset.CharsetBin |
|
$$ = x |
|
} |
|
| "VARBINARY" FieldLen |
|
{ |
|
x := types.NewFieldType(mysql.TypeVarchar) |
|
x.Flen = $2.(int) |
|
x.Charset = charset.CharsetBin |
|
x.Collate = charset.CharsetBin |
|
$$ = x |
|
} |
|
| BlobType |
|
{ |
|
$$ = $1.(*types.FieldType) |
|
} |
|
| TextType OptBinary OptCharset OptCollate |
|
{ |
|
x := $1.(*types.FieldType) |
|
if $2.(bool) { |
|
x.Flag |= mysql.BinaryFlag |
|
} |
|
x.Charset = $3.(string) |
|
x.Collate = $4.(string) |
|
$$ = x |
|
} |
|
| "ENUM" '(' StringList ')' OptCharset OptCollate |
|
{ |
|
x := types.NewFieldType(mysql.TypeEnum) |
|
x.Elems = $3.([]string) |
|
x.Charset = $5.(string) |
|
x.Collate = $6.(string) |
|
$$ = x |
|
} |
|
| "SET" '(' StringList ')' OptCharset OptCollate |
|
{ |
|
x := types.NewFieldType(mysql.TypeSet) |
|
x.Elems = $3.([]string) |
|
x.Charset = $5.(string) |
|
x.Collate = $6.(string) |
|
$$ = x |
|
} |
|
|
|
NationalOpt: |
|
{ |
|
|
|
} |
|
| "NATIONAL" |
|
{ |
|
|
|
} |
|
|
|
BlobType: |
|
"TINYBLOB" |
|
{ |
|
x := types.NewFieldType(mysql.TypeTinyBlob) |
|
x.Charset = charset.CharsetBin |
|
x.Collate = charset.CharsetBin |
|
$$ = x |
|
} |
|
| "BLOB" OptFieldLen |
|
{ |
|
x := types.NewFieldType(mysql.TypeBlob) |
|
x.Flen = $2.(int) |
|
x.Charset = charset.CharsetBin |
|
x.Collate = charset.CharsetBin |
|
$$ = x |
|
} |
|
| "MEDIUMBLOB" |
|
{ |
|
x := types.NewFieldType(mysql.TypeMediumBlob) |
|
x.Charset = charset.CharsetBin |
|
x.Collate = charset.CharsetBin |
|
$$ = x |
|
} |
|
| "LONGBLOB" |
|
{ |
|
x := types.NewFieldType(mysql.TypeLongBlob) |
|
x.Charset = charset.CharsetBin |
|
x.Collate = charset.CharsetBin |
|
$$ = 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 |
|
} |
|
|
|
|
|
DateAndTimeType: |
|
"DATE" |
|
{ |
|
x := types.NewFieldType(mysql.TypeDate) |
|
$$ = x |
|
} |
|
| "DATETIME" OptFieldLen |
|
{ |
|
x := types.NewFieldType(mysql.TypeDatetime) |
|
x.Decimal = $2.(int) |
|
$$ = x |
|
} |
|
| "TIMESTAMP" OptFieldLen |
|
{ |
|
x := types.NewFieldType(mysql.TypeTimestamp) |
|
x.Decimal = $2.(int) |
|
$$ = x |
|
} |
|
| "TIME" OptFieldLen |
|
{ |
|
x := types.NewFieldType(mysql.TypeDuration) |
|
x.Decimal = $2.(int) |
|
$$ = x |
|
} |
|
| "YEAR" OptFieldLen |
|
{ |
|
x := types.NewFieldType(mysql.TypeYear) |
|
x.Flen = $2.(int) |
|
$$ = x |
|
} |
|
|
|
FieldLen: |
|
'(' LengthNum ')' |
|
{ |
|
$$ = int($2.(uint64)) |
|
} |
|
|
|
OptFieldLen: |
|
{ |
|
/* -1 means unspecified field length*/ |
|
$$ = types.UnspecifiedLength |
|
} |
|
| FieldLen |
|
{ |
|
$$ = $1.(int) |
|
} |
|
|
|
FieldOpt: |
|
"UNSIGNED" |
|
{ |
|
$$ = &ast.TypeOpt{IsUnsigned: true} |
|
} |
|
| "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))} |
|
} |
|
|
|
OptBinary: |
|
{ |
|
$$ = false |
|
} |
|
| "BINARY" |
|
{ |
|
$$ = true |
|
} |
|
|
|
OptCharset: |
|
{ |
|
$$ = "" |
|
} |
|
| CharsetKw StringName |
|
{ |
|
$$ = $2.(string) |
|
} |
|
|
|
CharsetKw: |
|
"CHARACTER" "SET" |
|
| "CHARSET" |
|
|
|
OptCollate: |
|
{ |
|
$$ = "" |
|
} |
|
| "COLLATE" StringName |
|
{ |
|
$$ = $2.(string) |
|
} |
|
|
|
StringList: |
|
stringLit |
|
{ |
|
$$ = []string{$1.(string)} |
|
} |
|
| StringList ',' stringLit |
|
{ |
|
$$ = append($1.([]string), $3.(string)) |
|
} |
|
|
|
StringName: |
|
stringLit |
|
{ |
|
$$ = $1.(string) |
|
} |
|
| Identifier |
|
{ |
|
$$ = $1.(string) |
|
} |
|
|
|
/*********************************************************************************** |
|
* Update Statement |
|
* See: https://dev.mysql.com/doc/refman/5.7/en/update.html |
|
***********************************************************************************/ |
|
UpdateStmt: |
|
"UPDATE" LowPriorityOptional IgnoreOptional TableRef "SET" AssignmentList WhereClauseOptional OrderByOptional LimitClause |
|
{ |
|
var refs *ast.Join |
|
if x, ok := $4.(*ast.Join); ok { |
|
refs = x |
|
} else { |
|
refs = &ast.Join{Left: $4.(ast.ResultSetNode)} |
|
} |
|
st := &ast.UpdateStmt{ |
|
LowPriority: $2.(bool), |
|
TableRefs: &ast.TableRefsClause{TableRefs: refs}, |
|
List: $6.([]*ast.Assignment), |
|
} |
|
if $7 != nil { |
|
st.Where = $7.(ast.ExprNode) |
|
} |
|
if $8 != nil { |
|
st.Order = $8.(*ast.OrderByClause) |
|
} |
|
if $9 != nil { |
|
st.Limit = $9.(*ast.Limit) |
|
} |
|
$$ = st |
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
| "UPDATE" LowPriorityOptional IgnoreOptional TableRefs "SET" AssignmentList WhereClauseOptional |
|
{ |
|
st := &ast.UpdateStmt{ |
|
LowPriority: $2.(bool), |
|
TableRefs: &ast.TableRefsClause{TableRefs: $4.(*ast.Join)}, |
|
List: $6.([]*ast.Assignment), |
|
} |
|
if $7 != nil { |
|
st.Where = $7.(ast.ExprNode) |
|
} |
|
$$ = st |
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
|
|
UseStmt: |
|
"USE" DBName |
|
{ |
|
$$ = &ast.UseStmt{DBName: $2.(string)} |
|
if yylex.(*lexer).root { |
|
break |
|
} |
|
} |
|
|
|
WhereClause: |
|
"WHERE" Expression |
|
{ |
|
$$ = $2.(ast.ExprNode) |
|
} |
|
|
|
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 |
|
{ |
|
// See: https://dev.mysql.com/doc/refman/5.7/en/create-user.html |
|
$$ = &ast.CreateUserStmt{ |
|
IfNotExists: $3.(bool), |
|
Specs: $4.([]*ast.UserSpec), |
|
} |
|
} |
|
|
|
UserSpec: |
|
Username AuthOption |
|
{ |
|
userSpec := &ast.UserSpec{ |
|
User: $1.(string), |
|
} |
|
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)) |
|
} |
|
|
|
AuthOption: |
|
{ |
|
$$ = nil |
|
} |
|
| "IDENTIFIED" "BY" AuthString |
|
{ |
|
$$ = &ast.AuthOption { |
|
AuthString: $3.(string), |
|
ByAuthString: true, |
|
} |
|
} |
|
| "IDENTIFIED" "BY" "PASSWORD" HashString |
|
{ |
|
$$ = &ast.AuthOption{ |
|
HashString: $4.(string), |
|
} |
|
} |
|
|
|
HashString: |
|
stringLit |
|
|
|
/************************************************************************************* |
|
* Grant statement |
|
* See: https://dev.mysql.com/doc/refman/5.7/en/grant.html |
|
*************************************************************************************/ |
|
GrantStmt: |
|
"GRANT" PrivElemList "ON" ObjectType PrivLevel "TO" UserSpecList |
|
{ |
|
$$ = &ast.GrantStmt{ |
|
Privs: $2.([]*ast.PrivElem), |
|
ObjectType: $4.(ast.ObjectTypeType), |
|
Level: $5.(*ast.GrantLevel), |
|
Users: $7.([]*ast.UserSpec), |
|
} |
|
} |
|
|
|
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 |
|
} |
|
| "ALTER" |
|
{ |
|
$$ = mysql.AlterPriv |
|
} |
|
| "CREATE" |
|
{ |
|
$$ = mysql.CreatePriv |
|
} |
|
| "CREATE" "USER" |
|
{ |
|
$$ = mysql.CreateUserPriv |
|
} |
|
| "DELETE" |
|
{ |
|
$$ = mysql.DeletePriv |
|
} |
|
| "DROP" |
|
{ |
|
$$ = mysql.DropPriv |
|
} |
|
| "EXECUTE" |
|
{ |
|
$$ = mysql.ExecutePriv |
|
} |
|
| "INDEX" |
|
{ |
|
$$ = mysql.IndexPriv |
|
} |
|
| "INSERT" |
|
{ |
|
$$ = mysql.InsertPriv |
|
} |
|
| "SELECT" |
|
{ |
|
$$ = mysql.SelectPriv |
|
} |
|
| "SHOW" "DATABASES" |
|
{ |
|
$$ = mysql.ShowDBPriv |
|
} |
|
| "UPDATE" |
|
{ |
|
$$ = mysql.UpdatePriv |
|
} |
|
| "GRANT" "OPTION" |
|
{ |
|
$$ = mysql.GrantPriv |
|
} |
|
|
|
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.(string), |
|
} |
|
} |
|
| Identifier '.' Identifier |
|
{ |
|
$$ = &ast.GrantLevel { |
|
Level: ast.GrantLevelTable, |
|
DBName: $1.(string), |
|
TableName: $3.(string), |
|
} |
|
} |
|
| Identifier |
|
{ |
|
$$ = &ast.GrantLevel { |
|
Level: ast.GrantLevelTable, |
|
TableName: $1.(string), |
|
} |
|
} |
|
|
|
/********************************************************************* |
|
* 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" "TABLES" |
|
|
|
LockTablesStmt: |
|
"LOCK" "TABLES" TableLockList |
|
|
|
TableLock: |
|
TableName LockType |
|
|
|
LockType: |
|
"READ" |
|
| "READ" "LOCAL" |
|
| "WRITE" |
|
|
|
TableLockList: |
|
TableLock |
|
| TableLockList ',' TableLock |
|
|
|
%%
|
|
|