Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/semantic/op.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (t *translator) sqlTableExpr(e ast.SQLTableExpr, seq sem.Seq) (sem.Seq, rel
}
var typ super.Type
seq, typ, name = t.fromSource(input.Source, input.Args, seq)
table = newTableFromType(typ)
table = t.newTableFromType(typ)
if _, ok := table.(*dynamicTable); !ok && alias == nil {
alias = &ast.TableAlias{Name: name, Loc: input.Loc}
}
Expand Down
23 changes: 15 additions & 8 deletions compiler/semantic/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,35 +114,42 @@ type (
}
)

func newTableFromType(typ super.Type) relTable {
func (t *translator) newTableFromType(typ super.Type) relTable {
if typ == badType {
return badTable
}
if typ != nil {
if recType := recordOf(typ); recType != nil {
if recType := t.recordOf(typ); recType != nil {
return &staticTable{typ: recType}
}
}
return &dynamicTable{}
}

func recordOf(typ super.Type) *super.TypeRecord {
func (t *translator) recordOf(typ super.Type) *super.TypeRecord {
switch typ := super.TypeUnder(typ).(type) {
case *super.TypeRecord:
return typ
case *super.TypeUnion:
if hasUnknown(typ) {
return nil
}
// We assume here this is a fused type and thus has only one record
// type in the union.
// A fused type has at most one unnamed record type in a union, but
// the fuser keeps named types apart, so a union like foo|{b:int64}
// can have more than one record type. Fuse them so the table has
// the columns of all of them.
var recs []super.Type
for _, typ := range typ.Types {
if typ := recordOf(typ); typ != nil {
return typ
if typ := t.recordOf(typ); typ != nil {
recs = append(recs, typ)
}
}
if len(recs) > 0 {
recType, _ := super.TypeUnder(t.checker.fuse(recs)).(*super.TypeRecord)
return recType
}
case *super.TypeFusion:
return recordOf(typ.Type)
return t.recordOf(typ.Type)
}
return nil
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/semantic/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,9 @@ func (t *translator) selectFrom(loc ast.Loc, from ast.SQLTableExpr, seq sem.Seq,
// in which case we should model an empty table.
if inType == nil || inType == super.TypeNull {
// Model no or null input as an input table so that column checks fail.
return seq, newTableFromType(t.sctx.MustLookupTypeRecord([]super.Field{}))
return seq, t.newTableFromType(t.sctx.MustLookupTypeRecord([]super.Field{}))
}
return seq, newTableFromType(inType)
return seq, t.newTableFromType(inType)
}
off := len(seq)
hasParent := off > 0
Expand Down Expand Up @@ -372,7 +372,7 @@ func (t *translator) sqlFromPipe(pipe *ast.SQLPipe, seq sem.Seq) (sem.Seq, relTa
// join where the input to the pipe might depend on the table-expression to the left
// may change how we wire things up here.
seq, typ := t.seq(pipe.Body, super.TypeNull)
return seq, newTableFromType(typ)
return seq, t.newTableFromType(typ)
}

func maybeSQLQueryBody(pipe *ast.SQLPipe) (ast.SQLQueryBody, bool) {
Expand Down
17 changes: 14 additions & 3 deletions compiler/ztests/sql/issue-7468.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
# A SQL column reference to a field the BSUP reader projects from a record
# type that doesn't have it is null rather than an error.
# type that doesn't have it is null rather than an error, and the columns of
# input with a named record type include the fields of its other record types.
script: |
super -f bsup -o t1.bsup -c "type foo={a:int64} values cast({a:1},foo), {b:2}"
super -f bsup -o t2.bsup -c "values {a:1}, {b:2}"
super -s -c "SELECT * FROM 't1.bsup'"
echo ===
super -s -c "SELECT b FROM 't1.bsup'"
echo ===
super -s -c "SELECT a FROM 't2.bsup'"
echo ===
super -s -c "type foo={a:int64} type bar={b:int64} values cast({a:1},foo), cast({b:2},bar) | SELECT *"

outputs:
- name: stdout
data: |
{a:1}
{a:null}
{a:1,b:null}
{a:null,b:2}
===
{b:null}
{b:2}
===
{a:1}
{a:null}
===
{a:1,b:null}
{a:null,b:2}
Loading