diff --git a/intergration/radon-test/r/delete.result b/intergration/radon-test/r/delete.result index b8b9b60d..915d53a0 100644 --- a/intergration/radon-test/r/delete.result +++ b/intergration/radon-test/r/delete.result @@ -21,6 +21,29 @@ a b INSERT INTO integrate_test.t1 VALUES (1,1); +DELETE from integrate_test.t1 where t1.a=t1.b; + +select sleep(0.2); +sleep(0.2) +0 + +select * from integrate_test.t1; + +DELETE /*test error: unkown column*/ from integrate_test.t1 where x.a=t1.b; +ERROR 1054 (42S22): Unknown column 'x.a' in 'where clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=t1.b order by x.a; +ERROR 1054 (42S22): Unknown column 'x.a' in 'order clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=x.b; +ERROR 1054 (42S22): Unknown column 'x.b' in 'where clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=1 order by x.x.a; +ERROR 1054 (42S22): Unknown column 'x.x.a' in 'order clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where t1.a=t1.b limit a; +ERROR 1327 (42000): Undeclared variable: a + DELETE quick from integrate_test.t1; select * from integrate_test.t1; @@ -107,7 +130,7 @@ CREATE /*test error, column not exist*/ TABLE integrate_test.t1 ( ); DELETE FROM integrate_test.t1 WHERE post='1'; -ERROR 1054 (42S22): Unknown column 'post' in 'where clause' +ERROR 1054 (42S22): Unknown column 'integrate_test.t1_0032.post' in 'where clause' drop table integrate_test.t1; @@ -130,6 +153,24 @@ ERROR 1105 (HY000): unsupported: currently.not.support.partitions.in.delete DELETE /*test unsupport, currently not support delete with subquery*/ FROM integrate_test.t2 ORDER BY (SELECT x); ERROR 1105 (HY000): unsupported: subqueries.in.delete +DELETE /*test error: unkown column*/ from integrate_test.t1 where x.a=t2.b; +ERROR 1054 (42S22): Unknown column 'x.a' in 'where clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t2.a=t2.b order by x.a; +ERROR 1054 (42S22): Unknown column 'integrate_test.t2.a' in 'where clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t2.a=x.b; +ERROR 1054 (42S22): Unknown column 'integrate_test.t2.a' in 'where clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t2.a=1 order by x.x.a; +ERROR 1054 (42S22): Unknown column 'integrate_test.t2.a' in 'where clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where t2.a=t2.b limit a; +ERROR 1054 (42S22): Unknown column 't2.a' in 'where clause' + +DELETE quick from integrate_test.t1; +ERROR 1146 (42S02): Table 't1' doesn't exist + drop table integrate_test.t2; @@ -168,6 +209,23 @@ SELECT * FROM integrate_test.t1; a 0 +DELETE /*test error: unkown column*/ from integrate_test.t1 where x.a=t1.b; +ERROR 1054 (42S22): Unknown column 'x.a' in 'where clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=t1.b order by x.a; +ERROR 1054 (42S22): Unknown column 'x.a' in 'order clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=x.b; +ERROR 1054 (42S22): Unknown column 'x.b' in 'where clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=1 order by x.x.a; +ERROR 1054 (42S22): Unknown column 'x.x.a' in 'order clause' + +DELETE /*test error: unkown column*/ from integrate_test.t1 where t1.a=t1.b limit a; +ERROR 1327 (42000): Undeclared variable: a + +DELETE quick from integrate_test.t1; + DROP TABLE integrate_test.t1; diff --git a/intergration/radon-test/r/desc_explain.result b/intergration/radon-test/r/desc_explain.result index 5e9e6780..008aa04a 100644 --- a/intergration/radon-test/r/desc_explain.result +++ b/intergration/radon-test/r/desc_explain.result @@ -389,7 +389,7 @@ EXPLAIN: { "RawQuery": "explain delete from integrate_test.t where c1=2", "Partitions": [ { - "Query": "delete from integrate_test.t_0061 where c1 = 2", + "Query": "delete from integrate_test.t_0061 where integrate_test.t_0061.c1 = 2", "Backend": "backend2", "Range": "[3904-3968)" } diff --git a/intergration/radon-test/r/replace.result b/intergration/radon-test/r/replace.result index 983503f5..ab94c35d 100644 --- a/intergration/radon-test/r/replace.result +++ b/intergration/radon-test/r/replace.result @@ -172,19 +172,19 @@ c1 c2 2 4 3 5 -replace into integrate_test.t_list values (1,4), (7,9), (2,8); +replace into integrate_test.t_list values (5,4), (7,9), (8,8); -replace /*Column count doesn't match value count*/ into integrate_test.t_list values (1,4,8), (7,9), (2,8,0); +replace /*Column count doesn't match value count*/ into integrate_test.t_list values (1,4,8), (2,8,0); ERROR 1136 (21S01): Column count doesn't match value count at row 1 select * from integrate_test.t_list order by integrate_test.t_list.c1 asc; c1 c2 -1 4 1 2 -2 8 2 4 3 5 +5 4 7 9 +8 8 drop table integrate_test.t_list; diff --git a/intergration/radon-test/t/delete.test b/intergration/radon-test/t/delete.test index c8b9ef5a..c7247f95 100644 --- a/intergration/radon-test/t/delete.test +++ b/intergration/radon-test/t/delete.test @@ -9,6 +9,14 @@ DELETE from integrate_test.t1 where a=1 limit 1; DELETE LOW_PRIORITY from integrate_test.t1 where a=2; select * from integrate_test.t1; INSERT INTO integrate_test.t1 VALUES (1,1); +DELETE from integrate_test.t1 where t1.a=t1.b; +select sleep(0.2); +select * from integrate_test.t1; +DELETE /*test error: unkown column*/ from integrate_test.t1 where x.a=t1.b; +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=t1.b order by x.a; +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=x.b; +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=1 order by x.x.a; +DELETE /*test error: unkown column*/ from integrate_test.t1 where t1.a=t1.b limit a; DELETE quick from integrate_test.t1; select * from integrate_test.t1; drop table integrate_test.t1; @@ -60,6 +68,12 @@ select * from integrate_test.t2; delete /*test unsupport, currently not support delete multitables*/ integrate_test.t2 from integrate_test.t2, integrate_test.t2 as t1 where integrate_test.t2.b = integrate_test.t1.b and integrate_test.t2.a > integrate_test.t1.a; delete /*test unsupport, currently not support delete with partition*/ from integrate_test.t2 partition (p0) where a = 1; DELETE /*test unsupport, currently not support delete with subquery*/ FROM integrate_test.t2 ORDER BY (SELECT x); +DELETE /*test error: unkown column*/ from integrate_test.t1 where x.a=t2.b; +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t2.a=t2.b order by x.a; +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t2.a=x.b; +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t2.a=1 order by x.x.a; +DELETE /*test error: unkown column*/ from integrate_test.t1 where t2.a=t2.b limit a; +DELETE quick from integrate_test.t1; drop table integrate_test.t2; CREATE TABLE integrate_test.t1(a INTEGER PRIMARY KEY) single; @@ -77,6 +91,12 @@ DELETE FROM integrate_test.t1 WHERE a > 0 ORDER BY a; INSERT INTO integrate_test.t1 VALUES (0),(1),(2); DELETE FROM integrate_test.t1 WHERE a > 0 ORDER BY a LIMIT 1; SELECT * FROM integrate_test.t1; +DELETE /*test error: unkown column*/ from integrate_test.t1 where x.a=t1.b; +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=t1.b order by x.a; +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=x.b; +DELETE /*test error: unkown column*/ from integrate_test.t1 where integrate_test.t1.a=1 order by x.x.a; +DELETE /*test error: unkown column*/ from integrate_test.t1 where t1.a=t1.b limit a; +DELETE quick from integrate_test.t1; DROP TABLE integrate_test.t1; drop database integrate_test; diff --git a/intergration/radon-test/t/replace.test b/intergration/radon-test/t/replace.test index 126dd311..a4b3969b 100644 --- a/intergration/radon-test/t/replace.test +++ b/intergration/radon-test/t/replace.test @@ -77,8 +77,8 @@ create /*test partition list*/ table integrate_test.t_list(c1 int, c2 int) ENGIN insert into integrate_test.t_list values (1,2), (3,5), (2,4); insert /*test error: has no parition*/ into integrate_test.t_list values (10,2), (13,5); select * from integrate_test.t_list order by integrate_test.t_list.c1 asc; -replace into integrate_test.t_list values (1,4), (7,9), (2,8); -replace /*Column count doesn't match value count*/ into integrate_test.t_list values (1,4,8), (7,9), (2,8,0); +replace into integrate_test.t_list values (5,4), (7,9), (8,8); +replace /*Column count doesn't match value count*/ into integrate_test.t_list values (1,4,8), (2,8,0); select * from integrate_test.t_list order by integrate_test.t_list.c1 asc; drop table integrate_test.t_list; diff --git a/src/planner/common.go b/src/planner/common.go index 96503829..5bffbdb9 100644 --- a/src/planner/common.go +++ b/src/planner/common.go @@ -10,8 +10,11 @@ package planner import ( "errors" + "fmt" + "github.com/xelabs/go-mysqlstack/sqldb" "github.com/xelabs/go-mysqlstack/sqlparser" + "github.com/xelabs/go-mysqlstack/xlog" ) func hasSubquery(node sqlparser.SQLNode) bool { @@ -38,3 +41,83 @@ func isUpdateShardKey(exprs sqlparser.UpdateExprs, shardkey string) bool { } return false } + +// checkField is used to check if the field's Qualifier is illegal or not in where or order by clause. +func checkField(database, table string, node sqlparser.SQLNode, log *xlog.Log) error { + switch node.(type) { + case *sqlparser.Delete: + // Here we not should do check on limit clause, leave it to the backend mysql. + nodePtr := node.(*sqlparser.Delete) + if err := checkFieldImpl(database, table, nodePtr.Where, "where clause"); err != nil { + return err + } + if err := checkFieldImpl(database, table, nodePtr.OrderBy, "order clause"); err != nil { + return err + } + default: + log.Warning("currently.check.field.only.support.delete.statement.") + } + return nil +} + +// checkFieldImpl is an implementation of checkField() function. +func checkFieldImpl(database, table string, node sqlparser.SQLNode, suffix string) error { + err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + switch node.(type) { + case *sqlparser.ColName: + nodePtr := node.(*sqlparser.ColName) + colDB := nodePtr.Qualifier.Qualifier.String() + colTbl := nodePtr.Qualifier.Name.String() + colName := nodePtr.Name.String() + + if colDB != "" && colTbl != "" { + // case 1: where/order by db.t.a + if colDB != database || colTbl != table { + badField := fmt.Sprintf("%s.%s.%s", colDB, colTbl, colName) + return false, sqldb.NewSQLError(sqldb.ER_BAD_FIELD_ERROR, badField, suffix) + } + } else if colDB == "" && colTbl != "" { + // case 2: where/order by t.a + if colTbl != table { + badField := fmt.Sprintf("%s.%s", colTbl, colName) + return false, sqldb.NewSQLError(sqldb.ER_BAD_FIELD_ERROR, badField, suffix) + } + } + // case 3: where/order by a, return + return true, nil + default: + // If node is not sqlparser.ColName type, return true and continue visit. + return true, nil + } + }, node) + return err +} + +// rewriteField used to rewrite column field in where/order by clause. +// Currently only used by delete statement. +func rewriteField(database, partTable string, newNode sqlparser.SQLNode, log *xlog.Log) { + switch newNode.(type) { + case *sqlparser.Delete: + // Here we should not do rewrite on limit clause, leave it to the backend mysql. + nodePtr := newNode.(*sqlparser.Delete) + rewriteFieldImpl(database, partTable, nodePtr.Where, nodePtr.OrderBy) + default: + log.Warning("currently.rewrite.field.only.support.delete.statement.") + } +} + +// rewriteFieldImpl is an implementation of rewriteField() function +func rewriteFieldImpl(database, partTable string, newNodes ...sqlparser.SQLNode) { + sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + switch node.(type) { + case *sqlparser.ColName: + nodePtr := node.(*sqlparser.ColName) + nodePtr.Qualifier.Name = sqlparser.NewTableIdent(partTable) + nodePtr.Qualifier.Qualifier = sqlparser.NewTableIdent(database) + return true, nil + default: + // If newNode is not sqlparser.ColName type, return true and continue visit. + return true, nil + } + }, newNodes...) +} diff --git a/src/planner/delete_plan.go b/src/planner/delete_plan.go index 0519488c..af06e69b 100644 --- a/src/planner/delete_plan.go +++ b/src/planner/delete_plan.go @@ -82,10 +82,11 @@ func (p *DeletePlan) analyze() error { // Build used to build distributed querys. func (p *DeletePlan) Build() error { - if err := p.analyze(); err != nil { + // step 1: analyze if delete has unsupported features. + var err error + if err = p.analyze(); err != nil { return err } - newNode := *p.node // For single table, the len(TableRefs)=1 and the type of TableExpr must be AliasedTableExpr. newAliseExpr := newNode.TableRefs[0].(*sqlparser.AliasedTableExpr) @@ -97,8 +98,13 @@ func (p *DeletePlan) Build() error { newAliseExpr.Expr = sqlparser.TableName{Name: tableID, Qualifier: databaseID} } + // step 2: check if the column field illegal or not if delete statement has column field. + if err = checkField(databaseID.String(), tableID.String(), &newNode, p.log); err != nil { + return err + } + + // step 3: get segments var segments []router.Segment - var err error if newNode.Where == nil { // delete all datas, send sql to different backends, except for single table which has only one backend. segments, err = p.router.Lookup(databaseID.String(), tableID.String(), nil, nil) @@ -119,10 +125,13 @@ func (p *DeletePlan) Build() error { } } - // Rewritten the newNode to produce a new query. + // step 4: Rewritten the newNode to produce a new query. for _, segment := range segments { - tableID = sqlparser.NewTableIdent(segment.Table) - newAliseExpr.Expr = sqlparser.TableName{Name: tableID, Qualifier: databaseID} + // rewrite column field + rewriteField(databaseID.String(), segment.Table, &newNode, p.log) + // rewrite table expr + newTableID := sqlparser.NewTableIdent(segment.Table) + newAliseExpr.Expr = sqlparser.TableName{Name: newTableID, Qualifier: databaseID} tuple := xcontext.QueryTuple{ Query: sqlparser.String(&newNode), Backend: segment.Backend, diff --git a/src/planner/delete_plan_test.go b/src/planner/delete_plan_test.go index 9ab0f1e1..19307972 100644 --- a/src/planner/delete_plan_test.go +++ b/src/planner/delete_plan_test.go @@ -24,7 +24,7 @@ func TestDeletePlan(t *testing.T) { "RawQuery": "delete LOW_PRIORITY LOW_PRIORITY from sbtest.A where id=1", "Partitions": [ { - "Query": "delete low_priority low_priority from sbtest.A6 where id = 1", + "Query": "delete low_priority low_priority from sbtest.A6 where sbtest.A6.id = 1", "Backend": "backend6", "Range": "[512-4096)" } @@ -34,7 +34,7 @@ func TestDeletePlan(t *testing.T) { "RawQuery": "delete QUICK QUICK from sbtest.A where id=1 order by xx", "Partitions": [ { - "Query": "delete quick quick from sbtest.A6 where id = 1 order by xx asc", + "Query": "delete quick quick from sbtest.A6 where sbtest.A6.id = 1 order by sbtest.A6.xx asc", "Backend": "backend6", "Range": "[512-4096)" } @@ -44,32 +44,32 @@ func TestDeletePlan(t *testing.T) { "RawQuery": "delete IGNORE IGNORE from sbtest.A where name='xx'", "Partitions": [ { - "Query": "delete ignore ignore from sbtest.A1 where name = 'xx'", + "Query": "delete ignore ignore from sbtest.A1 where sbtest.A1.name = 'xx'", "Backend": "backend1", "Range": "[0-32)" }, { - "Query": "delete ignore ignore from sbtest.A2 where name = 'xx'", + "Query": "delete ignore ignore from sbtest.A2 where sbtest.A2.name = 'xx'", "Backend": "backend2", "Range": "[32-64)" }, { - "Query": "delete ignore ignore from sbtest.A3 where name = 'xx'", + "Query": "delete ignore ignore from sbtest.A3 where sbtest.A3.name = 'xx'", "Backend": "backend3", "Range": "[64-96)" }, { - "Query": "delete ignore ignore from sbtest.A4 where name = 'xx'", + "Query": "delete ignore ignore from sbtest.A4 where sbtest.A4.name = 'xx'", "Backend": "backend4", "Range": "[96-256)" }, { - "Query": "delete ignore ignore from sbtest.A5 where name = 'xx'", + "Query": "delete ignore ignore from sbtest.A5 where sbtest.A5.name = 'xx'", "Backend": "backend5", "Range": "[256-512)" }, { - "Query": "delete ignore ignore from sbtest.A6 where name = 'xx'", + "Query": "delete ignore ignore from sbtest.A6 where sbtest.A6.name = 'xx'", "Backend": "backend6", "Range": "[512-4096)" } @@ -79,7 +79,7 @@ func TestDeletePlan(t *testing.T) { "RawQuery": "delete LOW_PRIORITY QUICK IGNORE from sbtest.A where id in (1, 2,3)", "Partitions": [ { - "Query": "delete low_priority quick ignore from sbtest.A6 where id in (1, 2, 3)", + "Query": "delete low_priority quick ignore from sbtest.A6 where sbtest.A6.id in (1, 2, 3)", "Backend": "backend6", "Range": "[512-4096)" } @@ -89,12 +89,12 @@ func TestDeletePlan(t *testing.T) { "RawQuery": "delete from sbtest.G where id in (1, 2,3)", "Partitions": [ { - "Query": "delete from sbtest.G where id in (1, 2, 3)", + "Query": "delete from sbtest.G where sbtest.G.id in (1, 2, 3)", "Backend": "backend1", "Range": "" }, { - "Query": "delete from sbtest.G where id in (1, 2, 3)", + "Query": "delete from sbtest.G where sbtest.G.id in (1, 2, 3)", "Backend": "backend2", "Range": "" } @@ -104,7 +104,7 @@ func TestDeletePlan(t *testing.T) { "RawQuery": "delete from sbtest.S where id in (1, 2,3)", "Partitions": [ { - "Query": "delete from sbtest.S where id in (1, 2, 3)", + "Query": "delete from sbtest.S where sbtest.S.id in (1, 2, 3)", "Backend": "backend1", "Range": "" } @@ -114,32 +114,32 @@ func TestDeletePlan(t *testing.T) { "RawQuery": "delete from sbtest.A order by xx limit 1", "Partitions": [ { - "Query": "delete from sbtest.A1 order by xx asc limit 1", + "Query": "delete from sbtest.A1 order by sbtest.A1.xx asc limit 1", "Backend": "backend1", "Range": "[0-32)" }, { - "Query": "delete from sbtest.A2 order by xx asc limit 1", + "Query": "delete from sbtest.A2 order by sbtest.A2.xx asc limit 1", "Backend": "backend2", "Range": "[32-64)" }, { - "Query": "delete from sbtest.A3 order by xx asc limit 1", + "Query": "delete from sbtest.A3 order by sbtest.A3.xx asc limit 1", "Backend": "backend3", "Range": "[64-96)" }, { - "Query": "delete from sbtest.A4 order by xx asc limit 1", + "Query": "delete from sbtest.A4 order by sbtest.A4.xx asc limit 1", "Backend": "backend4", "Range": "[96-256)" }, { - "Query": "delete from sbtest.A5 order by xx asc limit 1", + "Query": "delete from sbtest.A5 order by sbtest.A5.xx asc limit 1", "Backend": "backend5", "Range": "[256-512)" }, { - "Query": "delete from sbtest.A6 order by xx asc limit 1", + "Query": "delete from sbtest.A6 order by sbtest.A6.xx asc limit 1", "Backend": "backend6", "Range": "[512-4096)" } @@ -149,12 +149,12 @@ func TestDeletePlan(t *testing.T) { "RawQuery": "delete from sbtest.G order by xx limit 2", "Partitions": [ { - "Query": "delete from sbtest.G order by xx asc limit 2", + "Query": "delete from sbtest.G order by sbtest.G.xx asc limit 2", "Backend": "backend1", "Range": "" }, { - "Query": "delete from sbtest.G order by xx asc limit 2", + "Query": "delete from sbtest.G order by sbtest.G.xx asc limit 2", "Backend": "backend2", "Range": "" } @@ -224,6 +224,10 @@ func TestDeleteUnsupportedPlan(t *testing.T) { "delete a from a join b on a.id = b.id where b.name = 'test'", "DELETE FROM t1, alias USING t1, t2 alias WHERE t1.a = alias.a", "delete from t partition (p0) where a = 1", + "delete from sbtest.A where x.id in (1,2,3)", + "delete from sbtest.A where x.A.id in (1,2,3)", + "delete from sbtest.A where sbtest.A.id in (1,2,3) order by sbtest.x.id", + "delete from sbtest.A where A.id in (1,2,3) order by x.A.id", } results := []string{ @@ -233,6 +237,10 @@ func TestDeleteUnsupportedPlan(t *testing.T) { "unsupported: currently.not.support.multitables.in.delete", "unsupported: currently.not.support.multitables.in.delete", "unsupported: currently.not.support.partitions.in.delete", + "Unknown column 'x.id' in 'where clause' (errno 1054) (sqlstate 42S22)", + "Unknown column 'x.A.id' in 'where clause' (errno 1054) (sqlstate 42S22)", + "Unknown column 'sbtest.x.id' in 'order clause' (errno 1054) (sqlstate 42S22)", + "Unknown column 'x.A.id' in 'order clause' (errno 1054) (sqlstate 42S22)", } log := xlog.NewStdLog(xlog.Level(xlog.PANIC)) diff --git a/src/vendor/github.com/xelabs/go-mysqlstack/sqldb/constants.go b/src/vendor/github.com/xelabs/go-mysqlstack/sqldb/constants.go index 68bc5a99..e76f01ab 100644 --- a/src/vendor/github.com/xelabs/go-mysqlstack/sqldb/constants.go +++ b/src/vendor/github.com/xelabs/go-mysqlstack/sqldb/constants.go @@ -293,6 +293,9 @@ const ( // ER_BAD_DB_ERROR enum. ER_TABLE_EXISTS_ERROR = 1050 + // ER_BAD_FIELD_ERROR enum. + ER_BAD_FIELD_ERROR = 1054 + // ER_TOO_LONG_IDENT enum ER_TOO_LONG_IDENT = 1059 @@ -346,6 +349,7 @@ var SQLErrors = map[uint16]*SQLError{ ER_NO_DB_ERROR: &SQLError{Num: ER_NO_DB_ERROR, State: "3D000", Message: "No database selected"}, ER_BAD_DB_ERROR: &SQLError{Num: ER_BAD_DB_ERROR, State: "42000", Message: "Unknown database '%-.192s'"}, ER_TABLE_EXISTS_ERROR: &SQLError{Num: ER_TABLE_EXISTS_ERROR, State: "42S01", Message: "Table '%s' already exists"}, + ER_BAD_FIELD_ERROR: &SQLError{Num: ER_BAD_FIELD_ERROR, State: "42S22", Message: "Unknown column '%s' in '%s'"}, ER_TOO_LONG_IDENT: &SQLError{Num: ER_TOO_LONG_IDENT, State: "42000", Message: "Identifier name '%-.100s' is too long"}, ER_KILL_DENIED_ERROR: &SQLError{Num: ER_KILL_DENIED_ERROR, State: "HY000", Message: "You are not owner of thread '%-.192s'"}, ER_UNKNOWN_ERROR: &SQLError{Num: ER_UNKNOWN_ERROR, State: "HY000", Message: "%v"},