github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/build_util.go (about) 1 // Copyright 2021 - 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package plan 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "strings" 22 23 "github.com/matrixorigin/matrixone/pkg/catalog" 24 "github.com/matrixorigin/matrixone/pkg/common/moerr" 25 "github.com/matrixorigin/matrixone/pkg/container/batch" 26 "github.com/matrixorigin/matrixone/pkg/container/types" 27 "github.com/matrixorigin/matrixone/pkg/defines" 28 "github.com/matrixorigin/matrixone/pkg/logutil" 29 "github.com/matrixorigin/matrixone/pkg/pb/plan" 30 "github.com/matrixorigin/matrixone/pkg/pb/timestamp" 31 "github.com/matrixorigin/matrixone/pkg/sql/colexec" 32 "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect" 33 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 34 "github.com/matrixorigin/matrixone/pkg/sql/util" 35 "github.com/matrixorigin/matrixone/pkg/vm/process" 36 ) 37 38 // func appendQueryNode(query *Query, node *Node) int32 { 39 // nodeID := int32(len(query.Nodes)) 40 // node.NodeId = nodeID 41 // query.Nodes = append(query.Nodes, node) 42 43 // return nodeID 44 // } 45 46 // reCheckifNeedLockWholeTable checks if the whole table needs to be locked based on the last node's statistics. 47 // It returns true if the out count of the last node is greater than the maximum lock count, otherwise it returns false. 48 func reCheckifNeedLockWholeTable(builder *QueryBuilder) { 49 lockService := builder.compCtx.GetProcess().LockService 50 if lockService == nil { 51 // MockCompilerContext 52 return 53 } 54 lockconfig := lockService.GetConfig() 55 56 for _, n := range builder.qry.Nodes { 57 if n.NodeType != plan.Node_LOCK_OP { 58 continue 59 } 60 if !n.LockTargets[0].LockTable { 61 reCheckIfNeed := n.Stats.Outcnt > float64(lockconfig.MaxLockRowCount) 62 if reCheckIfNeed { 63 logutil.Infof("Row lock upgraded to table lock for SQL : %s", builder.compCtx.GetRootSql()) 64 logutil.Infof("the outcnt stats is %f", n.Stats.Outcnt) 65 n.LockTargets[0].LockTable = reCheckIfNeed 66 } 67 } 68 } 69 } 70 71 // GetFunctionArgTypeStrFromAst function arg type do not have scale and width, it depends on the data that it process 72 func GetFunctionArgTypeStrFromAst(arg tree.FunctionArg) (string, error) { 73 argDecl := arg.(*tree.FunctionArgDecl) 74 return GetFunctionTypeStrFromAst(argDecl.Type) 75 } 76 77 func GetFunctionTypeStrFromAst(typRef tree.ResolvableTypeReference) (string, error) { 78 typ, err := getTypeFromAst(moerr.Context(), typRef) 79 if err != nil { 80 return "", err 81 } 82 ret := strings.ToLower(types.T(typ.Id).String()) 83 // do not display precision, because the choice of decimal64 or decimal128 is not exposed to user 84 if strings.HasPrefix(ret, "decimal") { 85 return "decimal", nil 86 } 87 return ret, nil 88 } 89 90 func getTypeFromAst(ctx context.Context, typ tree.ResolvableTypeReference) (plan.Type, error) { 91 if n, ok := typ.(*tree.T); ok { 92 switch defines.MysqlType(n.InternalType.Oid) { 93 case defines.MYSQL_TYPE_BIT: 94 return plan.Type{Id: int32(types.T_bit), Width: n.InternalType.DisplayWith, Scale: -1}, nil 95 case defines.MYSQL_TYPE_TINY: 96 if n.InternalType.Unsigned { 97 return plan.Type{Id: int32(types.T_uint8), Width: n.InternalType.Width, Scale: -1}, nil 98 } 99 return plan.Type{Id: int32(types.T_int8), Width: n.InternalType.Width, Scale: -1}, nil 100 case defines.MYSQL_TYPE_SHORT: 101 if n.InternalType.Unsigned { 102 return plan.Type{Id: int32(types.T_uint16), Width: n.InternalType.Width, Scale: -1}, nil 103 } 104 return plan.Type{Id: int32(types.T_int16), Width: n.InternalType.Width, Scale: -1}, nil 105 case defines.MYSQL_TYPE_LONG, defines.MYSQL_TYPE_INT24: 106 if n.InternalType.Unsigned { 107 return plan.Type{Id: int32(types.T_uint32), Width: n.InternalType.Width, Scale: -1}, nil 108 } 109 return plan.Type{Id: int32(types.T_int32), Width: n.InternalType.Width, Scale: -1}, nil 110 case defines.MYSQL_TYPE_LONGLONG: 111 if n.InternalType.Unsigned { 112 return plan.Type{Id: int32(types.T_uint64), Width: n.InternalType.Width, Scale: -1}, nil 113 } 114 return plan.Type{Id: int32(types.T_int64), Width: n.InternalType.Width, Scale: -1}, nil 115 case defines.MYSQL_TYPE_FLOAT: 116 return plan.Type{Id: int32(types.T_float32), Width: n.InternalType.DisplayWith, Scale: n.InternalType.Scale}, nil 117 case defines.MYSQL_TYPE_DOUBLE: 118 return plan.Type{Id: int32(types.T_float64), Width: n.InternalType.DisplayWith, Scale: n.InternalType.Scale}, nil 119 case defines.MYSQL_TYPE_STRING: 120 width := n.InternalType.DisplayWith 121 // for char type,if we didn't specify the length, 122 // the default width should be 1, and for varchar,it's 123 // the defaultMaxLength 124 fstr := strings.ToLower(n.InternalType.FamilyString) 125 if width == -1 { 126 // create table t1(a char) -> DisplayWith = -1;but get width=1 in MySQL and PgSQL 127 if fstr == "char" { 128 width = 1 129 } else { 130 width = types.MaxVarcharLen 131 } 132 } 133 if fstr == "char" && width > types.MaxCharLen { 134 return plan.Type{}, moerr.NewOutOfRange(ctx, "char", " typeLen is over the MaxCharLen: %v", types.MaxCharLen) 135 } else if fstr == "varchar" && width > types.MaxVarcharLen { 136 return plan.Type{}, moerr.NewOutOfRange(ctx, "varchar", " typeLen is over the MaxVarcharLen: %v", types.MaxVarcharLen) 137 } 138 if fstr == "char" { // type char 139 return plan.Type{Id: int32(types.T_char), Width: width}, nil 140 } 141 return plan.Type{Id: int32(types.T_varchar), Width: width}, nil 142 case defines.MYSQL_TYPE_VAR_STRING, defines.MYSQL_TYPE_VARCHAR: 143 width := n.InternalType.DisplayWith 144 // for char type,if we didn't specify the length, 145 // the default width should be 1, and for varchar,it's 146 // the defaultMaxLength 147 // Should always specify length to varbinary. 148 fstr := strings.ToLower(n.InternalType.FamilyString) 149 // Check explicit casting. 150 if fstr == "binary" && n.InternalType.Scale == -1 { 151 r := plan.Type{Id: int32(types.T_binary), Width: width} 152 r.Scale = -1 153 return r, nil 154 } 155 if width == -1 { 156 // create table t1(a char) -> DisplayWith = -1;but get width=1 in MySQL and PgSQL 157 if fstr == "char" || fstr == "binary" { 158 width = 1 159 } else if fstr == "vecf32" || fstr == "vecf64" { 160 width = types.MaxArrayDimension 161 } else { 162 width = types.MaxVarcharLen 163 } 164 } 165 166 if (fstr == "char" || fstr == "binary") && width > types.MaxCharLen { 167 return plan.Type{}, moerr.NewOutOfRange(ctx, fstr, " typeLen is over the MaxCharLen: %v", types.MaxCharLen) 168 } else if (fstr == "varchar" || fstr == "varbinary") && width > types.MaxVarcharLen { 169 return plan.Type{}, moerr.NewOutOfRange(ctx, fstr, " typeLen is over the MaxVarcharLen: %v", types.MaxVarcharLen) 170 } else if fstr == "vecf32" || fstr == "vecf64" { 171 if width > types.MaxArrayDimension { 172 return plan.Type{}, moerr.NewOutOfRange(ctx, fstr, " typeLen is over the MaxVectorLen : %v", types.MaxArrayDimension) 173 } 174 if width < 1 { 175 return plan.Type{}, moerr.NewOutOfRange(ctx, fstr, " typeLen cannot be less than 1") 176 } 177 } 178 switch fstr { 179 case "char": 180 return plan.Type{Id: int32(types.T_char), Width: width}, nil 181 case "binary": 182 return plan.Type{Id: int32(types.T_binary), Width: width}, nil 183 case "varchar": 184 return plan.Type{Id: int32(types.T_varchar), Width: width}, nil 185 case "vecf32": 186 return plan.Type{Id: int32(types.T_array_float32), Width: width}, nil 187 case "vecf64": 188 return plan.Type{Id: int32(types.T_array_float64), Width: width}, nil 189 } 190 // varbinary 191 return plan.Type{Id: int32(types.T_varbinary), Width: width}, nil 192 case defines.MYSQL_TYPE_DATE: 193 return plan.Type{Id: int32(types.T_date)}, nil 194 case defines.MYSQL_TYPE_TIME: 195 return plan.Type{Id: int32(types.T_time), Width: n.InternalType.DisplayWith, Scale: n.InternalType.Scale}, nil 196 case defines.MYSQL_TYPE_DATETIME: 197 // currently the ast's width for datetime's is 26, this is not accurate and may need revise, not important though, as we don't need it anywhere else except to differentiate empty vector.Typ. 198 return plan.Type{Id: int32(types.T_datetime), Width: n.InternalType.DisplayWith, Scale: n.InternalType.Scale}, nil 199 case defines.MYSQL_TYPE_TIMESTAMP: 200 return plan.Type{Id: int32(types.T_timestamp), Width: n.InternalType.DisplayWith, Scale: n.InternalType.Scale}, nil 201 case defines.MYSQL_TYPE_DECIMAL: 202 if n.InternalType.DisplayWith > 16 { 203 return plan.Type{Id: int32(types.T_decimal128), Width: n.InternalType.DisplayWith, Scale: n.InternalType.Scale}, nil 204 } 205 return plan.Type{Id: int32(types.T_decimal64), Width: n.InternalType.DisplayWith, Scale: n.InternalType.Scale}, nil 206 case defines.MYSQL_TYPE_BOOL: 207 return plan.Type{Id: int32(types.T_bool)}, nil 208 case defines.MYSQL_TYPE_BLOB: 209 return plan.Type{Id: int32(types.T_blob)}, nil 210 case defines.MYSQL_TYPE_TEXT: 211 return plan.Type{Id: int32(types.T_text)}, nil 212 case defines.MYSQL_TYPE_JSON: 213 return plan.Type{Id: int32(types.T_json)}, nil 214 case defines.MYSQL_TYPE_UUID: 215 return plan.Type{Id: int32(types.T_uuid)}, nil 216 case defines.MYSQL_TYPE_TINY_BLOB: 217 return plan.Type{Id: int32(types.T_blob)}, nil 218 case defines.MYSQL_TYPE_MEDIUM_BLOB: 219 return plan.Type{Id: int32(types.T_blob)}, nil 220 case defines.MYSQL_TYPE_LONG_BLOB: 221 return plan.Type{Id: int32(types.T_blob)}, nil 222 case defines.MYSQL_TYPE_ENUM: 223 if len(n.InternalType.EnumValues) > types.MaxEnumLen { 224 return plan.Type{}, moerr.NewNYI(ctx, "enum type out of max length") 225 } 226 if len(n.InternalType.EnumValues) == 0 { 227 return plan.Type{}, moerr.NewNYI(ctx, "enum type length err") 228 } 229 230 return plan.Type{Id: int32(types.T_enum), Enumvalues: strings.Join(n.InternalType.EnumValues, ",")}, nil 231 default: 232 return plan.Type{}, moerr.NewNYI(ctx, "data type: '%s'", tree.String(&n.InternalType, dialect.MYSQL)) 233 } 234 } 235 return plan.Type{}, moerr.NewInternalError(ctx, "unknown data type") 236 } 237 238 func buildDefaultExpr(col *tree.ColumnTableDef, typ plan.Type, proc *process.Process) (*plan.Default, error) { 239 nullAbility := true 240 var expr tree.Expr = nil 241 for _, attr := range col.Attributes { 242 if s, ok := attr.(*tree.AttributeNull); ok { 243 nullAbility = s.Is 244 break 245 } 246 } 247 248 for _, attr := range col.Attributes { 249 if s, ok := attr.(*tree.AttributeDefault); ok { 250 expr = s.Expr 251 break 252 } 253 } 254 255 if typ.Id == int32(types.T_json) { 256 if expr != nil && !isNullAstExpr(expr) { 257 return nil, moerr.NewNotSupported(proc.Ctx, fmt.Sprintf("JSON column '%s' cannot have default value", col.Name.Parts[0])) 258 } 259 } 260 if !nullAbility && isNullAstExpr(expr) { 261 return nil, moerr.NewInvalidInput(proc.Ctx, "invalid default value for column '%s'", col.Name.Parts[0]) 262 } 263 264 if expr == nil { 265 return &plan.Default{ 266 NullAbility: nullAbility, 267 Expr: nil, 268 OriginString: "", 269 }, nil 270 } 271 272 binder := NewDefaultBinder(proc.Ctx, nil, nil, typ, nil) 273 planExpr, err := binder.BindExpr(expr, 0, false) 274 if err != nil { 275 return nil, err 276 } 277 278 if defaultFunc := planExpr.GetF(); defaultFunc != nil { 279 if int(typ.Id) != int(types.T_uuid) && defaultFunc.Func.ObjName == "uuid" { 280 return nil, moerr.NewInvalidInput(proc.Ctx, "invalid default value for column '%s'", col.Name.Parts[0]) 281 } 282 } 283 284 defaultExpr, err := makePlan2CastExpr(proc.Ctx, planExpr, typ) 285 if err != nil { 286 return nil, err 287 } 288 289 // try to calculate default value, return err if fails 290 newExpr, err := ConstantFold(batch.EmptyForConstFoldBatch, DeepCopyExpr(defaultExpr), proc, false) 291 if err != nil { 292 return nil, err 293 } 294 295 fmtCtx := tree.NewFmtCtx(dialect.MYSQL, tree.WithSingleQuoteString()) 296 fmtCtx.PrintExpr(expr, expr, false) 297 return &plan.Default{ 298 NullAbility: nullAbility, 299 Expr: newExpr, 300 OriginString: fmtCtx.String(), 301 }, nil 302 } 303 304 func buildOnUpdate(col *tree.ColumnTableDef, typ plan.Type, proc *process.Process) (*plan.OnUpdate, error) { 305 var expr tree.Expr = nil 306 307 for _, attr := range col.Attributes { 308 if s, ok := attr.(*tree.AttributeOnUpdate); ok { 309 expr = s.Expr 310 break 311 } 312 } 313 314 if expr == nil { 315 return nil, nil 316 } 317 318 binder := NewDefaultBinder(proc.Ctx, nil, nil, typ, nil) 319 planExpr, err := binder.BindExpr(expr, 0, false) 320 if err != nil { 321 return nil, err 322 } 323 324 onUpdateExpr, err := makePlan2CastExpr(proc.Ctx, planExpr, typ) 325 if err != nil { 326 return nil, err 327 } 328 329 // try to calculate on update value, return err if fails 330 executor, err := colexec.NewExpressionExecutor(proc, onUpdateExpr) 331 if err != nil { 332 return nil, err 333 } 334 defer executor.Free() 335 _, err = executor.Eval(proc, []*batch.Batch{batch.EmptyForConstFoldBatch}) 336 if err != nil { 337 return nil, err 338 } 339 340 ret := &plan.OnUpdate{ 341 Expr: onUpdateExpr, 342 OriginString: tree.String(expr, dialect.MYSQL), 343 } 344 return ret, nil 345 } 346 347 func isNullExpr(expr *plan.Expr) bool { 348 if expr == nil { 349 return false 350 } 351 switch ef := expr.Expr.(type) { 352 case *plan.Expr_Lit: 353 return expr.Typ.Id == int32(types.T_any) && ef.Lit.Isnull 354 default: 355 return false 356 } 357 } 358 359 func isNullAstExpr(expr tree.Expr) bool { 360 if expr == nil { 361 return false 362 } 363 v, ok := expr.(*tree.NumVal) 364 return ok && v.ValType == tree.P_null 365 } 366 367 func convertValueIntoBool(name string, args []*Expr, isLogic bool) error { 368 if !isLogic && (len(args) != 2 || (args[0].Typ.Id != int32(types.T_bool) && args[1].Typ.Id != int32(types.T_bool))) { 369 return nil 370 } 371 for _, arg := range args { 372 if arg.Typ.Id == int32(types.T_bool) { 373 continue 374 } 375 switch ex := arg.Expr.(type) { 376 case *plan.Expr_Lit: 377 switch value := ex.Lit.Value.(type) { 378 case *plan.Literal_I64Val: 379 if value.I64Val == 0 { 380 ex.Lit.Value = &plan.Literal_Bval{Bval: false} 381 } else { 382 ex.Lit.Value = &plan.Literal_Bval{Bval: true} 383 } 384 arg.Typ.Id = int32(types.T_bool) 385 } 386 } 387 } 388 return nil 389 } 390 391 func getFunctionObjRef(funcID int64, name string) *ObjectRef { 392 return &ObjectRef{ 393 Obj: funcID, 394 ObjName: name, 395 } 396 } 397 398 // getAccountIds transforms the account names into account ids. 399 // if accounts is nil, return the id of the sys account. 400 // func getAccountIds(ctx CompilerContext, accounts tree.IdentifierList) ([]uint32, error) { 401 // var accountIds []uint32 402 // var err error 403 // if len(accounts) != 0 { 404 // accountNames := make([]string, len(accounts)) 405 // for i, account := range accounts { 406 // accountNames[i] = string(account) 407 // } 408 // accountIds, err = ctx.ResolveAccountIds(accountNames) 409 // if err != nil { 410 // return nil, err 411 // } 412 // } else { 413 // accountIds = []uint32{catalog.System_Account} 414 // } 415 // if len(accountIds) == 0 { 416 // return nil, moerr.NewInternalError(ctx.GetContext(), "need specify account for the cluster tables") 417 // } 418 // return accountIds, err 419 // } 420 421 // func getAccountInfoOfClusterTable(ctx CompilerContext, accounts tree.IdentifierList, tableDef *TableDef, isClusterTable bool) (*plan.ClusterTable, error) { 422 // var accountIds []uint32 423 // var columnIndexOfAccountId int32 = -1 424 // var err error 425 // if isClusterTable { 426 // accountIds, err = getAccountIds(ctx, accounts) 427 // if err != nil { 428 // return nil, err 429 // } 430 // for i, col := range tableDef.GetCols() { 431 // if util.IsClusterTableAttribute(col.Name) { 432 // if columnIndexOfAccountId >= 0 { 433 // return nil, moerr.NewInternalError(ctx.GetContext(), "there are two account_ids in the cluster table") 434 // } else { 435 // columnIndexOfAccountId = int32(i) 436 // } 437 // } 438 // } 439 440 // if columnIndexOfAccountId == -1 { 441 // return nil, moerr.NewInternalError(ctx.GetContext(), "there is no account_id in the cluster table") 442 // } else if columnIndexOfAccountId >= int32(len(tableDef.GetCols())) { 443 // return nil, moerr.NewInternalError(ctx.GetContext(), "the index of the account_id in the cluster table is invalid") 444 // } 445 // } else { 446 // if len(accounts) != 0 { 447 // return nil, moerr.NewInvalidInput(ctx.GetContext(), "can not specify the accounts for the non cluster table") 448 // } 449 // } 450 // return &plan.ClusterTable{ 451 // IsClusterTable: isClusterTable, 452 // AccountIDs: accountIds, 453 // ColumnIndexOfAccountId: columnIndexOfAccountId, 454 // }, nil 455 // } 456 457 func getDefaultExpr(ctx context.Context, d *plan.ColDef) (*Expr, error) { 458 if !d.Default.NullAbility && d.Default.Expr == nil && !d.Typ.AutoIncr { 459 return nil, moerr.NewInvalidInput(ctx, "invalid default value for column '%s'", d.Name) 460 } 461 if d.Default.Expr == nil { 462 return &Expr{ 463 Expr: &plan.Expr_Lit{ 464 Lit: &Const{ 465 Isnull: true, 466 }, 467 }, 468 Typ: plan.Type{ 469 Id: d.Typ.Id, 470 NotNullable: false, 471 }, 472 }, nil 473 } 474 return d.Default.Expr, nil 475 } 476 477 func judgeUnixTimestampReturnType(timestr string) types.T { 478 retDecimal := 0 479 if dotIdx := strings.LastIndex(timestr, "."); dotIdx >= 0 { 480 retDecimal = len(timestr) - dotIdx - 1 481 } 482 483 if retDecimal > 6 || retDecimal == -1 { 484 retDecimal = 6 485 } 486 487 if retDecimal == 0 { 488 return types.T_int64 489 } else { 490 return types.T_decimal128 491 } 492 } 493 494 // Get the primary key name of the table 495 func getTablePriKeyName(priKeyDef *plan.PrimaryKeyDef) string { 496 if priKeyDef == nil { 497 return "" 498 } else { 499 return priKeyDef.PkeyColName 500 } 501 } 502 503 // Check whether the table column name is an internal key 504 func checkTableColumnNameValid(name string) bool { 505 if name == catalog.Row_ID || name == catalog.CPrimaryKeyColName || 506 name == catalog.TableTailAttrCommitTs || name == catalog.TableTailAttrAborted || name == catalog.TableTailAttrPKVal { 507 return false 508 } 509 return true 510 } 511 512 // Check the expr has paramExpr 513 func checkExprHasParamExpr(exprs []tree.Expr) bool { 514 for _, expr := range exprs { 515 if _, ok := expr.(*tree.ParamExpr); ok { 516 return true 517 } else if e, ok := expr.(*tree.FuncExpr); ok { 518 return checkExprHasParamExpr(e.Exprs) 519 } 520 } 521 return false 522 } 523 524 // makeSelectList forms SELECT Clause "Select t.a,t.b,... " 525 func makeSelectList(table string, strs []string) string { 526 bb := strings.Builder{} 527 for i, str := range strs { 528 if i > 0 { 529 bb.WriteByte(',') 530 } 531 //table 532 bb.WriteByte('`') 533 bb.WriteString(table) 534 bb.WriteByte('`') 535 bb.WriteByte('.') 536 //column 537 bb.WriteByte('`') 538 bb.WriteString(str) 539 bb.WriteByte('`') 540 } 541 return bb.String() 542 } 543 544 // makeWhere forms WHERE Clause "Where t.a is not null and ..." 545 func makeWhere(table string, strs []string) string { 546 bb := strings.Builder{} 547 for i, str := range strs { 548 if i > 0 { 549 bb.WriteString(" and ") 550 } 551 //table 552 bb.WriteByte('`') 553 bb.WriteString(table) 554 bb.WriteByte('`') 555 bb.WriteByte('.') 556 //column 557 bb.WriteByte('`') 558 bb.WriteString(str) 559 bb.WriteByte('`') 560 //is not null 561 bb.WriteString(" is not null") 562 } 563 bb.WriteByte(' ') 564 return bb.String() 565 } 566 567 // colIdsToNames convert the colId to the col name 568 func colIdsToNames(ctx context.Context, colIds []uint64, colDefs []*plan.ColDef) ([]string, error) { 569 colId2Name := make(map[uint64]string) 570 for _, def := range colDefs { 571 colId2Name[def.ColId] = def.Name 572 } 573 names := make([]string, 0) 574 for _, colId := range colIds { 575 if name, has := colId2Name[colId]; !has { 576 return nil, moerr.NewInternalError(ctx, fmt.Sprintf("colId %d does exist", colId)) 577 } else { 578 names = append(names, name) 579 } 580 } 581 return names, nil 582 } 583 584 /* 585 genSqlForCheckFKConstraints generates the fk constraint checking sql. 586 587 basic logic of fk constraint check. 588 589 parent table: 590 T(a) 591 child table: 592 S(b) 593 foreign key (b) references T(a) 594 595 596 generated sql : 597 select count(*) == 0 from ( 598 select distinct S.b from S where S.b is not null 599 except 600 select distinct T.a from T 601 ) 602 if the result is true, then the fk constraint confirmed. 603 */ 604 func genSqlForCheckFKConstraints(ctx context.Context, 605 fkey *plan.ForeignKeyDef, 606 childDbName, childTblName string, colsOfChild []*plan.ColDef, 607 parentDbName, parentTblName string, colsOfParent []*plan.ColDef) (string, error) { 608 609 //fk column names 610 fkCols, err := colIdsToNames(ctx, fkey.Cols, colsOfChild) 611 if err != nil { 612 return "", err 613 } 614 //referred column names 615 referCols, err := colIdsToNames(ctx, fkey.ForeignCols, colsOfParent) 616 if err != nil { 617 return "", err 618 } 619 620 childTableClause := fmt.Sprintf("`%s`.`%s`", childDbName, childTblName) 621 parentTableClause := fmt.Sprintf("`%s`.`%s`", parentDbName, parentTblName) 622 where := fmt.Sprintf("where %s", makeWhere(childTblName, fkCols)) 623 except := fmt.Sprintf("select distinct %s from %s %s except select distinct %s from %s", 624 makeSelectList(childTblName, fkCols), 625 childTableClause, 626 where, 627 makeSelectList(parentTblName, referCols), 628 parentTableClause, 629 ) 630 631 //make detect sql 632 sql := strings.Join([]string{ 633 "select count(*) = 0 from (", 634 except, 635 ")", 636 }, " ") 637 return sql, nil 638 } 639 640 // genSqlsForCheckFKSelfRefer generates the fk constraint checking sql. 641 // the only difference between genSqlsForCheckFKSelfRefer and genSqlForCheckFKConstraints 642 // is the parent table and child table are same in the fk self refer. 643 func genSqlsForCheckFKSelfRefer(ctx context.Context, 644 dbName, tblName string, 645 cols []*plan.ColDef, fkeys []*plan.ForeignKeyDef) ([]string, error) { 646 ret := make([]string, 0) 647 for _, fkey := range fkeys { 648 if fkey.ForeignTbl != 0 { 649 continue 650 } 651 sql, err := genSqlForCheckFKConstraints(ctx, fkey, dbName, tblName, cols, dbName, tblName, cols) 652 if err != nil { 653 return nil, err 654 } 655 ret = append(ret, sql) 656 } 657 return ret, nil 658 } 659 660 func rewriteForCreateTableLike(stmt *tree.CreateTable, ctx CompilerContext) (newStmt tree.Statement, err error) { 661 oldTable := stmt.LikeTableName 662 newTable := stmt.Table 663 664 tblName := formatStr(string(oldTable.ObjectName)) 665 dbName := formatStr(string(oldTable.SchemaName)) 666 dbName, err = databaseIsValid(getSuitableDBName(dbName, ""), ctx, Snapshot{TS: ×tamp.Timestamp{}}) 667 if err != nil { 668 return nil, err 669 } 670 671 _, tableDef := ctx.Resolve(dbName, tblName, Snapshot{TS: ×tamp.Timestamp{}}) 672 if tableDef == nil { 673 return nil, moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 674 } 675 if tableDef.TableType == catalog.SystemViewRel || tableDef.TableType == catalog.SystemExternalRel || tableDef.TableType == catalog.SystemClusterRel { 676 return nil, moerr.NewInternalError(ctx.GetContext(), "%s.%s is not BASE TABLE", dbName, tblName) 677 } 678 679 var createStr string 680 if tableDef.TableType == catalog.SystemOrdinaryRel { 681 createStr = fmt.Sprintf("CREATE TABLE `%s` (", formatStr(string(newTable.ObjectName))) 682 } else if tblName == catalog.MO_DATABASE || tblName == catalog.MO_TABLES || tblName == catalog.MO_COLUMNS { 683 createStr = fmt.Sprintf("CREATE TABLE `%s` (", formatStr(string(newTable.ObjectName))) 684 } 685 686 rowCount := 0 687 var pkDefs []string 688 colIdToName := make(map[uint64]string) 689 for _, col := range tableDef.Cols { 690 if col.Hidden { 691 continue 692 } 693 colName := col.Name 694 colIdToName[col.ColId] = col.Name 695 if colName == catalog.Row_ID { 696 continue 697 } 698 699 nullOrNot := "NOT NULL" 700 // col.Default must be not nil 701 if len(col.Default.OriginString) > 0 { 702 if !col.Primary { 703 nullOrNot = "DEFAULT " + formatStr(col.Default.OriginString) 704 } 705 } else if col.Default.NullAbility { 706 nullOrNot = "DEFAULT NULL" 707 } 708 709 if col.Typ.AutoIncr { 710 nullOrNot = "NOT NULL AUTO_INCREMENT" 711 } 712 713 var hasAttrComment string 714 if col.Comment != "" { 715 hasAttrComment = " COMMENT '" + col.Comment + "'" 716 } 717 718 if rowCount == 0 { 719 createStr += "\n" 720 } else { 721 createStr += ",\n" 722 } 723 typ := types.T(col.Typ.Id).ToType() 724 typeStr := typ.String() 725 if typ.Oid.IsDecimal() { //after decimal fix,remove this 726 typeStr = fmt.Sprintf("DECIMAL(%d,%d)", col.Typ.Width, col.Typ.Scale) 727 } 728 if typ.Oid == types.T_varchar || typ.Oid == types.T_char || 729 typ.Oid == types.T_binary || typ.Oid == types.T_varbinary || 730 typ.Oid.IsArrayRelate() || typ.Oid == types.T_bit { 731 typeStr += fmt.Sprintf("(%d)", col.Typ.Width) 732 } 733 if typ.Oid.IsFloat() && col.Typ.Scale != -1 { 734 typeStr += fmt.Sprintf("(%d,%d)", col.Typ.Width, col.Typ.Scale) 735 } 736 737 if typ.Oid.IsEnum() { 738 enums := strings.Split(col.Typ.GetEnumvalues(), ",") 739 typeStr += "(" 740 for i, enum := range enums { 741 typeStr += fmt.Sprintf("'%s'", enum) 742 if i < len(enums)-1 { 743 typeStr += "," 744 } 745 } 746 typeStr += ")" 747 } 748 749 updateOpt := "" 750 if col.OnUpdate != nil && col.OnUpdate.Expr != nil { 751 updateOpt = " ON UPDATE " + col.OnUpdate.OriginString 752 } 753 createStr += fmt.Sprintf("`%s` %s %s%s%s", formatStr(colName), typeStr, nullOrNot, updateOpt, hasAttrComment) 754 rowCount++ 755 if col.Primary { 756 pkDefs = append(pkDefs, colName) 757 } 758 } 759 760 // If it is a composite primary key, get the component columns of the composite primary key 761 if tableDef.Pkey != nil && len(tableDef.Pkey.Names) > 1 { 762 pkDefs = append(pkDefs, tableDef.Pkey.Names...) 763 } 764 765 if len(pkDefs) != 0 { 766 pkStr := "PRIMARY KEY (" 767 for i, def := range pkDefs { 768 if i == len(pkDefs)-1 { 769 pkStr += fmt.Sprintf("`%s`", formatStr(def)) 770 } else { 771 pkStr += fmt.Sprintf("`%s`,", formatStr(def)) 772 } 773 } 774 pkStr += ")" 775 if rowCount != 0 { 776 createStr += ",\n" 777 } 778 createStr += pkStr 779 } 780 781 if tableDef.Indexes != nil { 782 783 // We only print distinct index names. This is used to avoid printing the same index multiple times for IVFFLAT or 784 // other multi-table indexes. 785 indexNames := make(map[string]bool) 786 787 for _, indexdef := range tableDef.Indexes { 788 if _, ok := indexNames[indexdef.IndexName]; ok { 789 continue 790 } else { 791 indexNames[indexdef.IndexName] = true 792 } 793 794 var indexStr string 795 if indexdef.Unique { 796 indexStr = "UNIQUE KEY " 797 } else { 798 indexStr = "KEY " 799 } 800 indexStr += fmt.Sprintf("`%s` ", formatStr(indexdef.IndexName)) 801 if !catalog.IsNullIndexAlgo(indexdef.IndexAlgo) { 802 indexStr += fmt.Sprintf("USING %s ", indexdef.IndexAlgo) 803 } 804 indexStr += "(" 805 i := 0 806 for _, part := range indexdef.Parts { 807 if catalog.IsAlias(part) { 808 continue 809 } 810 if i > 0 { 811 indexStr += "," 812 } 813 814 indexStr += fmt.Sprintf("`%s`", formatStr(part)) 815 i++ 816 } 817 818 indexStr += ")" 819 if indexdef.IndexAlgoParams != "" { 820 var paramList string 821 paramList, err = catalog.IndexParamsToStringList(indexdef.IndexAlgoParams) 822 if err != nil { 823 return nil, err 824 } 825 indexStr += paramList 826 } 827 if indexdef.Comment != "" { 828 indexdef.Comment = strings.Replace(indexdef.Comment, "'", "\\'", -1) 829 indexStr += fmt.Sprintf(" COMMENT '%s'", formatStr(indexdef.Comment)) 830 } 831 if rowCount != 0 { 832 createStr += ",\n" 833 } 834 createStr += indexStr 835 } 836 } 837 838 for _, fk := range tableDef.Fkeys { 839 colNames := make([]string, len(fk.Cols)) 840 for i, colId := range fk.Cols { 841 colNames[i] = colIdToName[colId] 842 } 843 844 var fkTableDef *TableDef 845 846 //fk self reference 847 if fk.ForeignTbl == 0 { 848 fkTableDef = tableDef 849 } else { 850 _, fkTableDef = ctx.ResolveById(fk.ForeignTbl, Snapshot{TS: ×tamp.Timestamp{}}) 851 } 852 853 fkColIdToName := make(map[uint64]string) 854 for _, col := range fkTableDef.Cols { 855 fkColIdToName[col.ColId] = col.Name 856 } 857 fkColNames := make([]string, len(fk.ForeignCols)) 858 for i, colId := range fk.ForeignCols { 859 fkColNames[i] = fkColIdToName[colId] 860 } 861 862 if rowCount != 0 { 863 createStr += ",\n" 864 } 865 createStr += fmt.Sprintf("CONSTRAINT `%s` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) ON DELETE %s ON UPDATE %s", 866 formatStr(fk.Name), strings.Join(colNames, "`,`"), formatStr(fkTableDef.Name), strings.Join(fkColNames, "`,`"), fk.OnDelete.String(), fk.OnUpdate.String()) 867 } 868 869 if rowCount != 0 { 870 createStr += "\n" 871 } 872 createStr += ")" 873 874 var comment string 875 var partition string 876 for _, def := range tableDef.Defs { 877 if proDef, ok := def.Def.(*plan.TableDef_DefType_Properties); ok { 878 for _, kv := range proDef.Properties.Properties { 879 if kv.Key == catalog.SystemRelAttr_Comment { 880 comment = " COMMENT='" + kv.Value + "'" 881 } 882 } 883 } 884 } 885 886 if tableDef.Partition != nil { 887 partition = ` ` + tableDef.Partition.PartitionMsg 888 } 889 890 createStr += comment 891 createStr += partition 892 893 if tableDef.ClusterBy != nil { 894 clusterby := " CLUSTER BY (" 895 if util.JudgeIsCompositeClusterByColumn(tableDef.ClusterBy.Name) { 896 //multi column clusterby 897 cbNames := util.SplitCompositeClusterByColumnName(tableDef.ClusterBy.Name) 898 for i, cbName := range cbNames { 899 if i != 0 { 900 clusterby += fmt.Sprintf(", `%s`", formatStr(cbName)) 901 } else { 902 clusterby += fmt.Sprintf("`%s`", formatStr(cbName)) 903 } 904 } 905 } else { 906 //single column cluster by 907 clusterby += fmt.Sprintf("`%s`", formatStr(tableDef.ClusterBy.Name)) 908 } 909 clusterby += ")" 910 createStr += clusterby 911 } 912 913 var buf bytes.Buffer 914 for _, ch := range createStr { 915 if ch == '"' { 916 buf.WriteRune('"') 917 } 918 buf.WriteRune(ch) 919 } 920 sql := buf.String() 921 return getRewriteSQLStmt(ctx, sql) 922 }