diff --git a/evaluator/builtin.go b/evaluator/builtin.go index aeb785d5a1792..e5831971a8860 100644 --- a/evaluator/builtin.go +++ b/evaluator/builtin.go @@ -120,6 +120,12 @@ var Funcs = map[string]Func{ "ifnull": {builtinIfNull, 2, 2}, "nullif": {builtinNullIf, 2, 2}, + // miscellaneous functions + // get_lock() and release_lock() is parsed but do nothing. + // It is used for preventing error in Ruby's activerecord migrations. + "get_lock": {builtinLock, 2, 2}, + "release_lock": {builtinReleaseLock, 1, 1}, + // only used by new plan ast.AndAnd: {builtinAndAnd, 2, 2}, ast.OrOr: {builtinOrOr, 2, 2}, diff --git a/evaluator/builtin_other.go b/evaluator/builtin_other.go index bf2095c1fe7e5..3944c9dbaf0c4 100644 --- a/evaluator/builtin_other.go +++ b/evaluator/builtin_other.go @@ -484,3 +484,17 @@ func builtinGetVar(args []types.Datum, ctx context.Context) (types.Datum, error) } return types.Datum{}, nil } + +// The lock function will do nothing. +// Warning: get_lock() function is parsed but ignored. +func builtinLock(args []types.Datum, _ context.Context) (d types.Datum, err error) { + d.SetInt64(1) + return d, nil +} + +// The release lock function will do nothing. +// Warning: release_lock() function is parsed but ignored. +func builtinReleaseLock(args []types.Datum, _ context.Context) (d types.Datum, err error) { + d.SetInt64(1) + return d, nil +} diff --git a/evaluator/builtin_test.go b/evaluator/builtin_test.go index 6b2742c7a241e..bf06a7601a063 100644 --- a/evaluator/builtin_test.go +++ b/evaluator/builtin_test.go @@ -81,3 +81,19 @@ func (s *testEvaluatorSuite) TestIsNullFunc(c *C) { c.Assert(err, IsNil) c.Assert(v.GetInt64(), Equals, int64(1)) } + +func (s *testEvaluatorSuite) TestLock(c *C) { + defer testleak.AfterTest(c)() + + v, err := builtinLock(types.MakeDatums(1), nil) + c.Assert(err, IsNil) + c.Assert(v.GetInt64(), Equals, int64(1)) + + v, err = builtinLock(types.MakeDatums(nil), nil) + c.Assert(err, IsNil) + c.Assert(v.GetInt64(), Equals, int64(1)) + + v, err = builtinReleaseLock(types.MakeDatums(1), nil) + c.Assert(err, IsNil) + c.Assert(v.GetInt64(), Equals, int64(1)) +} diff --git a/infoschema/tables.go b/infoschema/tables.go index 42ab70ea99146..9a65d28ee1a85 100644 --- a/infoschema/tables.go +++ b/infoschema/tables.go @@ -37,6 +37,7 @@ const ( catalogVal = "def" tableProfiling = "PROFILING" tablePartitions = "PARTITIONS" + tableKeyColumm = "KEY_COLUMN_USAGE" ) type columnInfo struct { @@ -197,6 +198,21 @@ var collationsCols = []columnInfo{ {"SORTLEN", mysql.TypeLonglong, 3, 0, nil, nil}, } +var keyColumnUsageCols = []columnInfo{ + {"CONSTRAINT_CATALOG", mysql.TypeVarchar, 512, mysql.NotNullFlag, nil, nil}, + {"CONSTRAINT_SCHEMA", mysql.TypeVarchar, 64, mysql.NotNullFlag, nil, nil}, + {"CONSTRAINT_NAME", mysql.TypeVarchar, 64, mysql.NotNullFlag, nil, nil}, + {"TABLE_CATALOG", mysql.TypeVarchar, 512, mysql.NotNullFlag, nil, nil}, + {"TABLE_SCHEMA", mysql.TypeVarchar, 64, mysql.NotNullFlag, nil, nil}, + {"TABLE_NAME", mysql.TypeVarchar, 64, mysql.NotNullFlag, nil, nil}, + {"COLUMN_NAME", mysql.TypeVarchar, 64, mysql.NotNullFlag, nil, nil}, + {"ORDINAL_POSITION", mysql.TypeLonglong, 10, mysql.NotNullFlag, nil, nil}, + {"POSITION_IN_UNIQUE_CONSTRAINT", mysql.TypeLonglong, 10, 0, nil, nil}, + {"REFERENCED_TABLE_SCHEMA", mysql.TypeVarchar, 64, 0, nil, nil}, + {"REFERENCED_TABLE_NAME", mysql.TypeVarchar, 64, 0, nil, nil}, + {"REFERENCED_COLUMN_NAME", mysql.TypeVarchar, 64, 0, nil, nil}, +} + // See https://dev.mysql.com/doc/refman/5.7/en/partitions-table.html var partitionsCols = []columnInfo{ {"TABLE_CATALOG", mysql.TypeVarchar, 512, 0, nil, nil}, @@ -481,6 +497,7 @@ var tableNameToColumns = map[string]([]columnInfo){ tableFiles: filesCols, tableProfiling: profilingCols, tablePartitions: partitionsCols, + tableKeyColumm: keyColumnUsageCols, } func createMemoryTable(meta *model.TableInfo, alloc autoid.Allocator) (table.Table, error) { diff --git a/parser/parser.y b/parser/parser.y index ad3d1c58c4323..89ade112ef4cc 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -164,6 +164,7 @@ import ( full "FULL" fulltext "FULLTEXT" ge ">=" + getLock "GET_LOCK" global "GLOBAL" grant "GRANT" grants "GRANTS" @@ -247,6 +248,7 @@ import ( redundant "REDUNDANT" references "REFERENCES" regexpKwd "REGEXP" + releaseLock "RELEASE_LOCK" repeat "REPEAT" repeatable "REPEATABLE" replace "REPLACE" @@ -1966,7 +1968,7 @@ NotKeywordToken: | "IFNULL" | "ISNULL" | "LAST_INSERT_ID" | "LCASE" | "LENGTH" | "LOCATE" | "LOWER" | "LTRIM" | "MAX" | "MICROSECOND" | "MIN" | "MINUTE" | "NULLIF" | "MONTH" | "MONTHNAME" | "NOW" | "POW" | "POWER" | "RAND" | "SECOND" | "SQL_CALC_FOUND_ROWS" | "SUBDATE" | "SUBSTRING" %prec lowerThanLeftParen | "SUBSTRING_INDEX" | "SUM" | "TRIM" | "RTRIM" | "UCASE" | "UPPER" | "VERSION" -| "WEEKDAY" | "WEEKOFYEAR" | "YEARWEEK" | "ROUND" | "STATS_PERSISTENT" +| "WEEKDAY" | "WEEKOFYEAR" | "YEARWEEK" | "ROUND" | "STATS_PERSISTENT" | "GET_LOCK" | "RELEASE_LOCK" /************************************************************************************ * @@ -2765,6 +2767,14 @@ FunctionCallNonKeyword: { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: $3.([]ast.ExprNode)} } +| "GET_LOCK" '(' Expression ',' Expression ')' + { + $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode), $5.(ast.ExprNode)}} + } +| "RELEASE_LOCK" '(' Expression ')' + { + $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} + } DateArithOpt: "DATE_ADD" diff --git a/parser/parser_test.go b/parser/parser_test.go index d43fe3824c66b..14c19ec142b9e 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -48,7 +48,7 @@ func (s *testParserSuite) TestSimple(c *C) { "delay_key_write", "isolation", "repeatable", "committed", "uncommitted", "only", "serializable", "level", "curtime", "variables", "dayname", "version", "btree", "hash", "row_format", "dynamic", "fixed", "compressed", "compact", "redundant", "sql_no_cache sql_no_cache", "sql_cache sql_cache", "action", "round", - "enable", "disable", "reverse", "space", "privileges", + "enable", "disable", "reverse", "space", "privileges", "get_lock", "release_lock", } for _, kw := range unreservedKws { src := fmt.Sprintf("SELECT %s FROM tbl;", kw) @@ -618,6 +618,10 @@ func (s *testParserSuite) TestBuiltin(c *C) { {`select adddate("2011-11-11 10:10:10.123456", 10)`, true}, {`select adddate("2011-11-11 10:10:10.123456", 0.10)`, true}, {`select adddate("2011-11-11 10:10:10.123456", "11,11")`, true}, + + // For misc functions + {`SELECT GET_LOCK('lock1',10);`, true}, + {`SELECT RELEASE_LOCK('lock1');`, true}, } s.RunTest(c, table) } diff --git a/parser/scanner.l b/parser/scanner.l index eb84ac563f015..7df35a76da8ed 100644 --- a/parser/scanner.l +++ b/parser/scanner.l @@ -378,6 +378,7 @@ found_rows {f}{o}{u}{n}{d}_{r}{o}{w}{s} from {f}{r}{o}{m} full {f}{u}{l}{l} fulltext {f}{u}{l}{l}{t}{e}{x}{t} +get_lock {g}{e}{t}_{l}{o}{c}{k} global {g}{l}{o}{b}{a}{l} grant {g}{r}{a}{n}{t} grants {g}{r}{a}{n}{t}{s} @@ -447,6 +448,7 @@ quarter {q}{u}{a}{r}{t}{e}{r} quick {q}{u}{i}{c}{k} rand {r}{a}{n}{d} read {r}{e}{a}{d} +release_lock {r}{e}{l}{e}{a}{s}{e}_{l}{o}{c}{k} repeat {r}{e}{p}{e}{a}{t} repeatable {r}{e}{p}{e}{a}{t}{a}{b}{l}{e} references {r}{e}{f}{e}{r}{e}{n}{c}{e}{s} @@ -985,11 +987,15 @@ redundant lval.item = string(l.val) return statsPersistent {status} lval.item = string(l.val) return status +{get_lock} lval.item = string(l.val) + return getLock {global} lval.item = string(l.val) return global {rand} lval.item = string(l.val) return rand {read} return read +{release_lock} lval.item = string(l.val) + return releaseLock {repeat} lval.item = string(l.val) return repeat {repeatable} lval.item = string(l.val) diff --git a/plan/typeinferer.go b/plan/typeinferer.go index d11f1dea1262b..330f47058104a 100644 --- a/plan/typeinferer.go +++ b/plan/typeinferer.go @@ -298,6 +298,8 @@ func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) { // expr2 or expr3 returns a floating-point value floating-point // expr2 or expr3 returns an integer integer tp = x.Args[1].GetType() + case "get_lock", "release_lock": + tp = types.NewFieldType(mysql.TypeLonglong) default: tp = types.NewFieldType(mysql.TypeUnspecified) }