github.com/dolthub/go-mysql-server@v0.18.0/sql/planbuilder/from.go (about) 1 // Copyright 2023 Dolthub, Inc. 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 planbuilder 16 17 import ( 18 "fmt" 19 "strings" 20 21 ast "github.com/dolthub/vitess/go/vt/sqlparser" 22 23 "github.com/dolthub/go-mysql-server/sql" 24 "github.com/dolthub/go-mysql-server/sql/expression" 25 "github.com/dolthub/go-mysql-server/sql/mysql_db" 26 "github.com/dolthub/go-mysql-server/sql/plan" 27 "github.com/dolthub/go-mysql-server/sql/transform" 28 "github.com/dolthub/go-mysql-server/sql/types" 29 ) 30 31 // TODO outScope will be populated with a source node and column sets 32 func (b *Builder) buildFrom(inScope *scope, te ast.TableExprs) (outScope *scope) { 33 if len(te) == 0 { 34 outScope = inScope.push() 35 outScope.ast = te 36 outScope.node = plan.NewResolvedDualTable() 37 // new unreferenceable column to mirror empty table schema 38 outScope.addColumn(scopeColumn{table: "dual"}) 39 return 40 } 41 42 if len(te) > 1 { 43 cj := &ast.JoinTableExpr{ 44 LeftExpr: te[0], 45 RightExpr: te[1], 46 Join: ast.JoinStr, 47 Condition: ast.JoinCondition{On: ast.BoolVal(true)}, 48 } 49 for _, t := range te[2:] { 50 cj = &ast.JoinTableExpr{ 51 LeftExpr: cj, 52 RightExpr: t, 53 Join: ast.JoinStr, 54 Condition: ast.JoinCondition{On: ast.BoolVal(true)}, 55 } 56 } 57 return b.buildJoin(inScope, cj) 58 } 59 return b.buildDataSource(inScope, te[0]) 60 } 61 62 func (b *Builder) isLateral(te ast.TableExpr) bool { 63 switch t := te.(type) { 64 case *ast.JSONTableExpr: 65 return true 66 case *ast.AliasedTableExpr: 67 return t.Lateral 68 default: 69 return false 70 } 71 } 72 73 func (b *Builder) isUsingJoin(te *ast.JoinTableExpr) bool { 74 return te.Condition.Using != nil || 75 strings.EqualFold(te.Join, ast.NaturalJoinStr) || 76 strings.EqualFold(te.Join, ast.NaturalLeftJoinStr) || 77 strings.EqualFold(te.Join, ast.NaturalRightJoinStr) 78 } 79 80 func (b *Builder) buildJoin(inScope *scope, te *ast.JoinTableExpr) (outScope *scope) { 81 //TODO build individual table expressions 82 // collect column definitions 83 leftScope := b.buildDataSource(inScope, te.LeftExpr) 84 85 // TODO lateral join right will see left outputs 86 rightInScope := inScope 87 if b.isLateral(te.RightExpr) && te.Join != ast.RightJoinStr { 88 rightInScope = leftScope 89 } 90 rightScope := b.buildDataSource(rightInScope, te.RightExpr) 91 92 if b.isUsingJoin(te) { 93 return b.buildUsingJoin(inScope, leftScope, rightScope, te) 94 } 95 96 outScope = inScope.push() 97 outScope.appendColumnsFromScope(leftScope) 98 outScope.appendColumnsFromScope(rightScope) 99 100 // cross join 101 if (te.Condition.On == nil || te.Condition.On == ast.BoolVal(true)) && te.Condition.Using == nil { 102 if rast, ok := te.RightExpr.(*ast.AliasedTableExpr); ok && rast.Lateral { 103 var err error 104 outScope.node, err = b.f.buildJoin(leftScope.node, rightScope.node, plan.JoinTypeLateralCross, expression.NewLiteral(true, types.Boolean)) 105 if err != nil { 106 b.handleErr(err) 107 } 108 } else if b.isLateral(te.RightExpr) { 109 outScope.node = plan.NewJoin(leftScope.node, rightScope.node, plan.JoinTypeLateralCross, nil) 110 } else { 111 outScope.node = plan.NewCrossJoin(leftScope.node, rightScope.node) 112 } 113 return 114 } 115 116 var filter sql.Expression 117 if te.Condition.On != nil { 118 filter = b.buildScalar(outScope, te.Condition.On) 119 } 120 121 var op plan.JoinType 122 switch strings.ToLower(te.Join) { 123 case ast.JoinStr: 124 if b.isLateral(te.RightExpr) { 125 op = plan.JoinTypeLateralInner 126 } else { 127 op = plan.JoinTypeInner 128 } 129 case ast.LeftJoinStr: 130 if b.isLateral(te.RightExpr) { 131 op = plan.JoinTypeLateralLeft 132 } else { 133 op = plan.JoinTypeLeftOuter 134 } 135 case ast.RightJoinStr: 136 if b.isLateral(te.RightExpr) { 137 op = plan.JoinTypeLateralRight 138 } else { 139 op = plan.JoinTypeRightOuter 140 } 141 case ast.FullOuterJoinStr: 142 op = plan.JoinTypeFullOuter 143 default: 144 b.handleErr(fmt.Errorf("unknown join type: %s", te.Join)) 145 } 146 var err error 147 outScope.node, err = b.f.buildJoin(leftScope.node, rightScope.node, op, filter) 148 if err != nil { 149 b.handleErr(err) 150 } 151 152 return outScope 153 } 154 155 // buildUsingJoin converts a JOIN with a USING clause into an INNER JOIN, LEFT JOIN, or RIGHT JOIN; NATURAL JOINs are a 156 // subset of USING joins. 157 // The scope of these join must contain all the qualified columns from both left and right tables. The columns listed 158 // in the USING clause must be in both left and right tables, and will be redirected to 159 // either the left or right table. 160 // An equality filter is created with columns in the USING list. Columns in the USING 161 // list are de-duplicated and listed first (in the order they appear in the left table), followed by the remaining 162 // columns from the left table, followed by the remaining columns from the right table. 163 // NATURAL_JOIN(t1, t2) => PROJ(t1.a1, ...,t1.aN) -> INNER_JOIN(t1, t2, [t1.a1=t2.a1,..., t1.aN=t2.aN]) 164 // NATURAL_LEFT_JOIN(t1, t2) => PROJ(t1.a1, ...,t1.aN) -> LEFT_JOIN (t1, t2, [t1.a1=t2.a1,..., t1.aN=t2.aN]) 165 // NATURAL_RIGHT_JOIN(t1, t2) => PROJ(t1.a1, ...,t1.aN) -> RIGHT_JOIN(t1, t2, [t1.a1=t2.a1,..., t1.aN=t2.aN]) 166 // USING_JOIN(t1, t2) => PROJ(t1.a1, ...,t1.aN) -> INNER_JOIN(t1, t2, [t1.a1=t2.a1,..., t1.aN=t2.aN]) 167 // USING_LEFT_JOIN(t1, t2) => PROJ(t1.a1, ...,t1.aN) -> LEFT_JOIN (t1, t2, [t1.a1=t2.a1,..., t1.aN=t2.aN]) 168 // USING_RIGHT_JOIN(t1, t2) => PROJ(t1.a1, ...,t1.aN) -> RIGHT_JOIN(t1, t2, [t1.a1=t2.a1,..., t1.aN=t2.aN]) 169 func (b *Builder) buildUsingJoin(inScope, leftScope, rightScope *scope, te *ast.JoinTableExpr) (outScope *scope) { 170 outScope = inScope.push() 171 172 // Fill in USING columns for NATURAL JOINs 173 if len(te.Condition.Using) == 0 { 174 for _, lCol := range leftScope.cols { 175 for _, rCol := range rightScope.cols { 176 if strings.EqualFold(lCol.col, rCol.col) { 177 te.Condition.Using = append(te.Condition.Using, ast.NewColIdent(lCol.col)) 178 break 179 } 180 } 181 } 182 } 183 184 // Right joins swap left and right scopes. 185 var left, right *scope 186 if te.Join == ast.RightJoinStr || te.Join == ast.NaturalRightJoinStr { 187 left, right = rightScope, leftScope 188 } else { 189 left, right = leftScope, rightScope 190 } 191 192 // Add columns in common 193 var filter sql.Expression 194 usingCols := map[string]struct{}{} 195 for _, col := range te.Condition.Using { 196 colName := col.String() 197 // Every column in the USING clause must be in both tables. 198 lCol, ok := left.resolveColumn("", "", colName, false, false) 199 if !ok { 200 b.handleErr(sql.ErrUnknownColumn.New(colName, "from clause")) 201 } 202 rCol, ok := right.resolveColumn("", "", colName, false, false) 203 if !ok { 204 b.handleErr(sql.ErrUnknownColumn.New(colName, "from clause")) 205 } 206 f := expression.NewEquals(lCol.scalarGf(), rCol.scalarGf()) 207 if filter == nil { 208 filter = f 209 } else { 210 filter = expression.NewAnd(filter, f) 211 } 212 usingCols[colName] = struct{}{} 213 outScope.redirect(scopeColumn{col: rCol.col}, lCol) 214 } 215 216 // Add common columns first, then left, then right. 217 // The order of columns for the common section must match left table 218 for _, lCol := range left.cols { 219 if _, ok := usingCols[lCol.col]; ok { 220 outScope.addColumn(lCol) 221 } 222 } 223 for _, rCol := range right.cols { 224 if _, ok := usingCols[rCol.col]; ok { 225 outScope.addColumn(rCol) 226 } 227 } 228 for _, lCol := range left.cols { 229 if _, ok := usingCols[lCol.col]; !ok { 230 outScope.addColumn(lCol) 231 } 232 } 233 for _, rCol := range right.cols { 234 if _, ok := usingCols[rCol.col]; !ok { 235 outScope.addColumn(rCol) 236 } 237 } 238 239 // joining two tables with no common columns is just cross join 240 if len(te.Condition.Using) == 0 { 241 if b.isLateral(te.RightExpr) { 242 outScope.node = plan.NewJoin(leftScope.node, rightScope.node, plan.JoinTypeLateralCross, nil) 243 } else { 244 outScope.node = plan.NewCrossJoin(leftScope.node, rightScope.node) 245 } 246 return outScope 247 } 248 249 switch strings.ToLower(te.Join) { 250 case ast.JoinStr, ast.NaturalJoinStr: 251 outScope.node = plan.NewInnerJoin(leftScope.node, rightScope.node, filter) 252 case ast.LeftJoinStr, ast.NaturalLeftJoinStr: 253 outScope.node = plan.NewLeftOuterJoin(leftScope.node, rightScope.node, filter) 254 case ast.RightJoinStr, ast.NaturalRightJoinStr: 255 outScope.node = plan.NewLeftOuterJoin(rightScope.node, leftScope.node, filter) 256 default: 257 b.handleErr(fmt.Errorf("unknown using join type: %s", te.Join)) 258 } 259 return outScope 260 } 261 262 func (b *Builder) buildDataSource(inScope *scope, te ast.TableExpr) (outScope *scope) { 263 outScope = inScope.push() 264 outScope.ast = te 265 266 // build individual table, collect column definitions 267 switch t := (te).(type) { 268 case *ast.AliasedTableExpr: 269 switch e := t.Expr.(type) { 270 case ast.TableName: 271 tableName := strings.ToLower(e.Name.String()) 272 tAlias := strings.ToLower(t.As.String()) 273 if cteScope := inScope.getCte(tableName); cteScope != nil { 274 outScope = cteScope.aliasCte(tAlias) 275 outScope.parent = inScope 276 } else { 277 var ok bool 278 outScope, ok = b.buildTablescan(inScope, e.Qualifier.String(), tableName, t.AsOf) 279 if !ok { 280 b.handleErr(sql.ErrTableNotFound.New(tableName)) 281 } 282 } 283 if tAlias != "" { 284 outScope.setTableAlias(tAlias) 285 var err error 286 outScope.node, err = b.f.buildTableAlias(tAlias, outScope.node.(plan.TableIdNode)) 287 if err != nil { 288 b.handleErr(err) 289 } 290 } 291 case *ast.Subquery: 292 if t.As.IsEmpty() { 293 // This should be caught by the parser, but here just in case 294 b.handleErr(sql.ErrUnsupportedFeature.New("subquery without alias")) 295 } 296 297 sqScope := inScope.pushSubquery() 298 fromScope := b.buildSelectStmt(sqScope, e.Select) 299 alias := strings.ToLower(t.As.String()) 300 sq := plan.NewSubqueryAlias(alias, ast.String(e.Select), fromScope.node) 301 sq = sq.WithCorrelated(sqScope.correlated()) 302 sq = sq.WithVolatile(sqScope.volatile()) 303 sq.IsLateral = t.Lateral 304 305 var renameCols []string 306 if len(e.Columns) > 0 { 307 renameCols = columnsToStrings(e.Columns) 308 sq = sq.WithColumnNames(renameCols) 309 } 310 311 if len(renameCols) > 0 && len(fromScope.cols) != len(renameCols) { 312 err := sql.ErrColumnCountMismatch.New() 313 b.handleErr(err) 314 } 315 316 outScope = inScope.push() 317 tabId := outScope.addTable(sq.Name()) 318 319 scopeMapping := make(map[sql.ColumnId]sql.Expression) 320 var colSet sql.ColSet 321 for i, c := range fromScope.cols { 322 col := c.col 323 if len(renameCols) > 0 { 324 col = renameCols[i] 325 } 326 toId := outScope.newColumn(scopeColumn{ 327 tableId: tabId, 328 db: c.db, 329 table: alias, 330 col: col, 331 originalCol: c.originalCol, 332 id: 0, 333 typ: c.typ, 334 nullable: c.nullable, 335 }) 336 colSet.Add(sql.ColumnId(toId)) 337 scopeMapping[sql.ColumnId(toId)] = c.scalarGf() 338 } 339 outScope.node = sq.WithScopeMapping(scopeMapping).WithColumns(colSet).WithId(tabId) 340 return 341 case *ast.ValuesStatement: 342 if t.As.IsEmpty() { 343 // Parser should enforce this, but just to be safe 344 b.handleErr(sql.ErrUnsupportedSyntax.New("every derived table must have an alias")) 345 } 346 exprTuples := make([][]sql.Expression, len(e.Rows)) 347 for i, vt := range e.Rows { 348 exprs := make([]sql.Expression, len(vt)) 349 exprTuples[i] = exprs 350 for j, e := range vt { 351 exprs[j] = b.buildScalar(inScope, e) 352 } 353 } 354 355 outScope = inScope.push() 356 vdt := plan.NewValueDerivedTable(plan.NewValues(exprTuples), t.As.String()) 357 tableName := strings.ToLower(t.As.String()) 358 tabId := outScope.addTable(tableName) 359 var cols sql.ColSet 360 for _, c := range vdt.Schema() { 361 id := outScope.newColumn(scopeColumn{col: c.Name, db: c.DatabaseSource, table: tableName, typ: c.Type, nullable: c.Nullable}) 362 cols.Add(sql.ColumnId(id)) 363 } 364 var renameCols []string 365 if len(e.Columns) > 0 { 366 renameCols = columnsToStrings(e.Columns) 367 vdt = vdt.WithColumNames(renameCols) 368 } 369 b.renameSource(outScope, tableName, renameCols) 370 outScope.node = vdt.WithId(tabId).WithColumns(cols) 371 return 372 default: 373 b.handleErr(sql.ErrUnsupportedSyntax.New(ast.String(te))) 374 } 375 376 case *ast.TableFuncExpr: 377 return b.buildTableFunc(inScope, t) 378 379 case *ast.JoinTableExpr: 380 return b.buildJoin(inScope, t) 381 382 case *ast.JSONTableExpr: 383 return b.buildJSONTable(inScope, t) 384 385 case *ast.ParenTableExpr: 386 if len(t.Exprs) == 1 { 387 switch j := t.Exprs[0].(type) { 388 case *ast.JoinTableExpr: 389 return b.buildJoin(inScope, j) 390 default: 391 b.handleErr(sql.ErrUnsupportedSyntax.New(ast.String(t))) 392 } 393 } else { 394 b.handleErr(sql.ErrUnsupportedSyntax.New(ast.String(t))) 395 } 396 default: 397 b.handleErr(sql.ErrUnsupportedSyntax.New(ast.String(te))) 398 } 399 return 400 } 401 402 func columnsToStrings(cols ast.Columns) []string { 403 if len(cols) == 0 { 404 return nil 405 } 406 res := make([]string, len(cols)) 407 for i, c := range cols { 408 res[i] = c.String() 409 } 410 411 return res 412 } 413 414 func (b *Builder) resolveTable(tab, db string, asOf interface{}) *plan.ResolvedTable { 415 var table sql.Table 416 var database sql.Database 417 var err error 418 if asOf != nil { 419 table, database, err = b.cat.TableAsOf(b.ctx, db, tab, asOf) 420 } else { 421 table, database, err = b.cat.Table(b.ctx, db, tab) 422 } 423 if sql.ErrAsOfNotSupported.Is(err) { 424 if asOf != nil { 425 b.handleErr(err) 426 } 427 table, database, err = b.cat.Table(b.ctx, db, tab) 428 } 429 if err != nil { 430 b.handleErr(err) 431 } 432 433 if privilegedDatabase, ok := database.(mysql_db.PrivilegedDatabase); ok { 434 database = privilegedDatabase.Unwrap() 435 } 436 return plan.NewResolvedTable(table, database, asOf) 437 } 438 439 func (b *Builder) buildTableFunc(inScope *scope, t *ast.TableFuncExpr) (outScope *scope) { 440 //TODO what are valid mysql table arguments 441 args := make([]sql.Expression, 0, len(t.Exprs)) 442 for _, e := range t.Exprs { 443 switch e := e.(type) { 444 case *ast.AliasedExpr: 445 expr := b.buildScalar(inScope, e.Expr) 446 447 if !e.As.IsEmpty() { 448 b.handleErr(sql.ErrUnsupportedSyntax.New(ast.String(e))) 449 } 450 451 if selectExprNeedsAlias(e, expr) { 452 b.handleErr(sql.ErrUnsupportedSyntax.New(ast.String(e))) 453 } 454 455 args = append(args, expr) 456 default: 457 b.handleErr(sql.ErrUnsupportedSyntax.New(ast.String(e))) 458 } 459 } 460 461 utf := expression.NewUnresolvedTableFunction(t.Name, args) 462 463 tableFunction, err := b.cat.TableFunction(b.ctx, utf.Name()) 464 if err != nil { 465 b.handleErr(err) 466 } 467 468 database := b.currentDb() 469 470 var hasBindVarArgs bool 471 for _, arg := range utf.Arguments { 472 if _, ok := arg.(*expression.BindVar); ok { 473 hasBindVarArgs = true 474 break 475 } 476 } 477 478 outScope = inScope.push() 479 outScope.ast = t 480 if hasBindVarArgs { 481 // TODO deferred tableFunction 482 } 483 484 newInstance, err := tableFunction.NewInstance(b.ctx, database, utf.Arguments) 485 if err != nil { 486 b.handleErr(err) 487 } 488 489 if ctf, isCTF := newInstance.(sql.CatalogTableFunction); isCTF { 490 newInstance, err = ctf.WithCatalog(b.cat) 491 if err != nil { 492 b.handleErr(err) 493 } 494 } 495 496 // Table Function must always have an alias, pick function name as alias if none is provided 497 var name string 498 var newAlias plan.TableIdNode 499 if t.Alias.IsEmpty() { 500 name = t.Name 501 newAlias = plan.NewTableAlias(name, newInstance) 502 } else { 503 name = t.Alias.String() 504 newAlias, err = b.f.buildTableAlias(name, newInstance) 505 if err != nil { 506 b.handleErr(err) 507 } 508 } 509 510 tabId := outScope.addTable(name) 511 var colset sql.ColSet 512 for _, c := range newAlias.Schema() { 513 id := outScope.newColumn(scopeColumn{ 514 db: database.Name(), 515 table: name, 516 col: c.Name, 517 typ: c.Type, 518 }) 519 colset.Add(sql.ColumnId(id)) 520 } 521 outScope.node = newAlias.WithColumns(colset).WithId(tabId) 522 return 523 } 524 525 func (b *Builder) buildJSONTableCols(inScope *scope, jtSpec *ast.JSONTableSpec) []plan.JSONTableCol { 526 var cols []plan.JSONTableCol 527 for _, jtColDef := range jtSpec.Columns { 528 // nested col defs need to be flattened into multiple colOpts with all paths appended 529 if jtColDef.Spec != nil { 530 nestedCols := b.buildJSONTableCols(inScope, jtColDef.Spec) 531 col := plan.JSONTableCol{ 532 Path: jtColDef.Spec.Path, 533 NestedCols: nestedCols, 534 } 535 cols = append(cols, col) 536 continue 537 } 538 539 typ, err := types.ColumnTypeToType(&jtColDef.Type) 540 if err != nil { 541 b.handleErr(err) 542 } 543 544 var defEmptyVal, defErrorVal sql.Expression 545 if jtColDef.Opts.ValOnEmpty == nil { 546 defEmptyVal = expression.NewLiteral(nil, types.Null) 547 } else { 548 defEmptyVal = b.buildScalar(inScope, jtColDef.Opts.ValOnEmpty) 549 } 550 551 if jtColDef.Opts.ValOnError == nil { 552 defErrorVal = expression.NewLiteral(nil, types.Null) 553 } else { 554 defErrorVal = b.buildScalar(inScope, jtColDef.Opts.ValOnError) 555 } 556 557 col := plan.JSONTableCol{ 558 Path: jtColDef.Opts.Path, 559 Opts: &plan.JSONTableColOpts{ 560 Name: jtColDef.Name.String(), 561 Type: typ, 562 ForOrd: bool(jtColDef.Type.Autoincrement), 563 Exists: jtColDef.Opts.Exists, 564 DefEmptyVal: defEmptyVal, 565 DefErrorVal: defErrorVal, 566 ErrorOnEmpty: jtColDef.Opts.ErrorOnEmpty, 567 ErrorOnError: jtColDef.Opts.ErrorOnError, 568 }, 569 } 570 cols = append(cols, col) 571 } 572 return cols 573 } 574 575 func (b *Builder) buildJSONTable(inScope *scope, t *ast.JSONTableExpr) (outScope *scope) { 576 data := b.buildScalar(inScope, t.Data) 577 if _, ok := data.(*plan.Subquery); ok { 578 b.handleErr(sql.ErrInvalidArgument.New("JSON_TABLE")) 579 } 580 581 outScope = inScope.push() 582 outScope.ast = t 583 584 alias := strings.ToLower(t.Alias.String()) 585 tabId := outScope.addTable(alias) 586 cols := b.buildJSONTableCols(inScope, t.Spec) 587 var colset sql.ColSet 588 var recFlatten func(col plan.JSONTableCol) 589 recFlatten = func(col plan.JSONTableCol) { 590 for _, col := range col.NestedCols { 591 recFlatten(col) 592 } 593 if col.Opts != nil { 594 id := outScope.newColumn(scopeColumn{ 595 table: alias, 596 col: col.Opts.Name, 597 typ: col.Opts.Type, 598 }) 599 colset.Add(sql.ColumnId(id)) 600 } 601 } 602 for _, col := range cols { 603 recFlatten(col) 604 } 605 606 var err error 607 jt, err := plan.NewJSONTable(data, t.Spec.Path, alias, cols) 608 if err != nil { 609 b.handleErr(err) 610 } 611 612 outScope.node = jt.WithColumns(colset).WithId(tabId) 613 return outScope 614 } 615 616 func (b *Builder) buildTablescan(inScope *scope, db, name string, asof *ast.AsOf) (outScope *scope, ok bool) { 617 return b.buildResolvedTable(inScope, db, name, asof) 618 } 619 620 func (b *Builder) buildResolvedTable(inScope *scope, db, name string, asof *ast.AsOf) (outScope *scope, ok bool) { 621 outScope = inScope.push() 622 623 if db == "" && b.ViewCtx().DbName != "" { 624 db = b.ViewCtx().DbName 625 } else if db == "" { 626 db = b.ctx.GetCurrentDatabase() 627 } 628 629 var asOfLit interface{} 630 if asof != nil { 631 asOfLit = b.buildAsOfLit(inScope, asof.Time) 632 } else if asof := b.ViewCtx().AsOf; asof != nil { 633 asOfLit = asof 634 } else if asof := b.ProcCtx().AsOf; asof != nil { 635 asOfLit = asof 636 } 637 638 var tab sql.Table 639 var database sql.Database 640 var err error 641 database, err = b.cat.Database(b.ctx, db) 642 if err != nil { 643 b.handleErr(err) 644 } 645 646 if view := b.resolveView(name, database, asOfLit); view != nil { 647 outScope.node = view 648 tabId := outScope.addTable(strings.ToLower(view.Schema()[0].Name)) 649 var cols sql.ColSet 650 for _, c := range view.Schema() { 651 id := outScope.newColumn(scopeColumn{ 652 db: db, 653 table: name, 654 col: strings.ToLower(c.Name), 655 originalCol: c.Name, 656 typ: c.Type, 657 nullable: c.Nullable, 658 }) 659 cols.Add(sql.ColumnId(id)) 660 } 661 if tin, ok := view.(plan.TableIdNode); ok { 662 // TODO should *sql.View implement TableIdNode? 663 outScope.node = tin.WithId(tabId).WithColumns(cols) 664 } 665 666 return outScope, true 667 } 668 669 if asOfLit != nil { 670 tab, database, err = b.cat.TableAsOf(b.ctx, db, name, asOfLit) 671 } else { 672 tab, _, err = database.GetTableInsensitive(b.ctx, name) 673 } 674 if err != nil { 675 if sql.ErrDatabaseNotFound.Is(err) { 676 if db == "" { 677 err = sql.ErrNoDatabaseSelected.New() 678 } 679 } 680 b.handleErr(err) 681 } else if tab == nil { 682 if b.TriggerCtx().Active && !b.TriggerCtx().Call { 683 outScope.node = plan.NewUnresolvedTable(name, db) 684 b.TriggerCtx().UnresolvedTables = append(b.TriggerCtx().UnresolvedTables, name) 685 return outScope, true 686 } 687 return outScope, false 688 } 689 690 // TODO: this is maybe too broad for this method, we don't need this for some statements 691 if tab.Schema().HasVirtualColumns() { 692 tab = b.buildVirtualTableScan(db, tab) 693 } 694 695 rt := plan.NewResolvedTable(tab, database, asOfLit) 696 ct, ok := rt.Table.(sql.CatalogTable) 697 if ok { 698 rt.Table = ct.AssignCatalog(b.cat) 699 } 700 701 tabId := outScope.addTable(strings.ToLower(tab.Name())) 702 var cols sql.ColSet 703 704 for _, c := range tab.Schema() { 705 id := outScope.newColumn(scopeColumn{ 706 db: db, 707 table: strings.ToLower(tab.Name()), 708 col: strings.ToLower(c.Name), 709 originalCol: c.Name, 710 typ: c.Type, 711 nullable: c.Nullable, 712 }) 713 cols.Add(sql.ColumnId(id)) 714 } 715 716 rt = rt.WithId(tabId).WithColumns(cols).(*plan.ResolvedTable) 717 outScope.node = rt 718 719 if dt, _ := rt.Table.(sql.DynamicColumnsTable); dt != nil { 720 // the columns table has to resolve all columns in every table 721 sch, err := dt.AllColumns(b.ctx) 722 if err != nil { 723 b.handleErr(err) 724 } 725 726 var newSch sql.Schema 727 startSource := sch[0].Source 728 tmpScope := inScope.push() 729 for i, c := range sch { 730 // bucket schema fragments into colsets for resolving defaults 731 newCol := scopeColumn{ 732 db: c.DatabaseSource, 733 table: c.Source, 734 col: strings.ToLower(c.Name), 735 originalCol: c.Name, 736 typ: c.Type, 737 nullable: c.Nullable, 738 } 739 if !strings.EqualFold(c.Source, startSource) { 740 startSource = c.Source 741 tmpSch := b.resolveSchemaDefaults(tmpScope, sch[i-len(tmpScope.cols):i]) 742 newSch = append(newSch, tmpSch...) 743 tmpScope = inScope.push() 744 } 745 tmpScope.newColumn(newCol) 746 } 747 if len(tmpScope.cols) > 0 { 748 tmpSch := b.resolveSchemaDefaults(tmpScope, sch[len(sch)-len(tmpScope.cols):len(sch)]) 749 newSch = append(newSch, tmpSch...) 750 } 751 rt.Table, err = dt.WithDefaultsSchema(newSch) 752 if err != nil { 753 b.handleErr(err) 754 } 755 } 756 757 return outScope, true 758 } 759 760 func (b *Builder) resolveView(name string, database sql.Database, asOf interface{}) sql.Node { 761 var view *sql.View 762 763 if vdb, vok := database.(sql.ViewDatabase); vok { 764 viewDef, vdok, err := vdb.GetViewDefinition(b.ctx, name) 765 if err != nil { 766 b.handleErr(err) 767 } 768 oldOpts := b.parserOpts 769 defer func() { 770 b.parserOpts = oldOpts 771 }() 772 if vdok { 773 outerAsOf := b.ViewCtx().AsOf 774 outerDb := b.ViewCtx().DbName 775 b.ViewCtx().AsOf = asOf 776 b.ViewCtx().DbName = database.Name() 777 defer func() { 778 b.ViewCtx().AsOf = outerAsOf 779 b.ViewCtx().DbName = outerDb 780 }() 781 b.parserOpts = sql.NewSqlModeFromString(viewDef.SqlMode).ParserOptions() 782 node, _, _, err := b.Parse(viewDef.CreateViewStatement, false) 783 if err != nil { 784 // TODO: Need to account for non-existing functions or 785 // users without appropriate privilege to the referenced table/column/function. 786 if sql.ErrTableNotFound.Is(err) || sql.ErrColumnNotFound.Is(err) { 787 // TODO: ALTER VIEW should not return this error 788 err = sql.ErrInvalidRefInView.New(database.Name(), name) 789 } 790 b.handleErr(err) 791 } 792 create, ok := node.(*plan.CreateView) 793 if !ok { 794 err = fmt.Errorf("expected create view statement, found: %T", node) 795 } 796 switch n := create.Child.(type) { 797 case *plan.SubqueryAlias: 798 view = n.AsView(viewDef.CreateViewStatement) 799 default: 800 view = plan.NewSubqueryAlias(name, viewDef.TextDefinition, n).AsView(viewDef.CreateViewStatement) 801 } 802 803 } 804 } 805 // If we didn't find the view from the database directly, use the in-session registry 806 if view == nil { 807 view, _ = b.ctx.GetViewRegistry().View(database.Name(), name) 808 if view != nil { 809 def, _, _ := transform.NodeWithOpaque(view.Definition(), func(n sql.Node) (sql.Node, transform.TreeIdentity, error) { 810 // TODO this is a hack because the test registry setup is busted, these should always be resolved 811 if urt, ok := n.(*plan.UnresolvedTable); ok { 812 return b.resolveTable(urt.Name(), urt.Database().Name(), urt.AsOf()), transform.NewTree, nil 813 } 814 return n, transform.SameTree, nil 815 }) 816 view = view.WithDefinition(def) 817 } 818 } 819 820 if view == nil { 821 return nil 822 } 823 824 query := view.Definition().Children()[0] 825 n, err := view.Definition().WithChildren(query) 826 if err != nil { 827 b.handleErr(err) 828 } 829 return n 830 }