Skip to content
Draft
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
29 changes: 18 additions & 11 deletions enginetest/memory_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,23 +200,30 @@ func TestSingleQueryPrepared(t *testing.T) {

// Convenience test for debugging a single query. Unskip and set to the desired query.
func TestSingleScript(t *testing.T) {
t.Skip()
//t.Skip()
var scripts = []queries.ScriptTest{
{
Name: "AS OF propagates to nested CALLs",
SetUpScript: []string{},
Name: "AS OF propagates to nested CALLs",
SetUpScript: []string{
`CREATE TABLE test (pk INT PRIMARY KEY, v1 VARCHAR(255));`,
`INSERT INTO test VALUES (1, 'a'), (2, 'b');`,
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "create procedure create_proc() create table t (i int primary key, j int);",
Expected: []sql.Row{
{types.NewOkResult(0)},
},
Query: "SELECT temporarytesting(t) FROM test AS t;",
Expected: []sql.Row{},
},
{
Query: "call create_proc()",
Expected: []sql.Row{
{types.NewOkResult(0)},
},
Query: "SELECT temporarytesting(test) FROM test;",
Expected: []sql.Row{},
},
{
Query: "SELECT temporarytesting(pk, test) FROM test;",
Expected: []sql.Row{},
},
{
Query: "SELECT temporarytesting(v1, test, pk) FROM test;",
Expected: []sql.Row{},
},
},
},
Expand Down
49 changes: 49 additions & 0 deletions sql/expression/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,52 @@ func (e *Alias) WithChildren(children ...sql.Expression) (sql.Expression, error)

// Name implements the Nameable interface.
func (e *Alias) Name() string { return e.name }

// TODO: DELETE EVERYTHING UNDER HERE -----------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------

// This is an expression that will be returned from a Doltgres hook (GMS' hook will return a nil expression to indicate
// incompatibility). This function is just a stand-in for testing purposes.
type DoltgresHookExpression struct {
args []sql.Expression
}

var _ sql.Expression = (*DoltgresHookExpression)(nil)

func NewDoltgresHookExpression(args ...sql.Expression) sql.Expression {
return &DoltgresHookExpression{args: args}
}

func (tt *DoltgresHookExpression) String() string { return "temporarytesting2" }

// Type implements the Expression interface.
func (tt *DoltgresHookExpression) Type() sql.Type { return types.Int32 }

// Eval implements the Expression interface.
func (tt *DoltgresHookExpression) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
rowLen := len(row)
return int32(-rowLen), nil
}

// Resolved implements the Expression interface.
func (tt *DoltgresHookExpression) Resolved() bool {
return true
}

// Children implements the Expression interface.
func (tt *DoltgresHookExpression) Children() []sql.Expression {
return tt.args
}

// IsNullable implements the Expression interface.
func (tt *DoltgresHookExpression) IsNullable() bool {
return false
}

// WithChildren implements the Expression interface.
func (*DoltgresHookExpression) WithChildren(children ...sql.Expression) (sql.Expression, error) {
return NewDoltgresHookExpression(children...), nil
}
64 changes: 64 additions & 0 deletions sql/expression/function/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/dolthub/go-mysql-server/sql/expression/function/json"
"github.com/dolthub/go-mysql-server/sql/expression/function/spatial"
"github.com/dolthub/go-mysql-server/sql/expression/function/vector"
"github.com/dolthub/go-mysql-server/sql/types"
)

// ErrFunctionAlreadyRegistered is thrown when a function is already registered
Expand Down Expand Up @@ -342,6 +343,7 @@ var BuiltIns = []sql.Function{
sql.Function1{Name: "weekofyear", Fn: NewWeekOfYear},
sql.Function1{Name: "year", Fn: NewYear},
sql.FunctionN{Name: "yearweek", Fn: NewYearWeek},
sql.FunctionN{Name: "temporarytesting", Fn: NewTemporaryTesting}, // TODO: DELETE ME
}

func GetLockingFuncs(ls *sql.LockSubsystem) []sql.Function {
Expand Down Expand Up @@ -390,3 +392,65 @@ func (r Registry) mustRegister(fn ...sql.Function) {
panic(err)
}
}

// TODO: DELETE EVERYTHING UNDER HERE -----------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------

// This is an example Doltgres function. This exists solely for testing purposes, and will be deleted as noted by the
// massive comment above this.
type DoltgresFunction struct {
args []sql.Expression
}

var _ sql.FunctionExpression = (*DoltgresFunction)(nil)

func NewTemporaryTesting(args ...sql.Expression) (sql.Expression, error) {
return &DoltgresFunction{args: args}, nil
}

// FunctionName implements sql.FunctionExpression
func (tt *DoltgresFunction) FunctionName() string {
return "temporarytesting"
}

// Description implements sql.FunctionExpression
func (tt *DoltgresFunction) Description() string {
return ""
}

func (tt *DoltgresFunction) String() string { return "temporarytesting()" }

// Type implements the Expression interface.
func (tt *DoltgresFunction) Type() sql.Type { return types.Int32 }

// CollationCoercibility implements the interface sql.CollationCoercible.
func (*DoltgresFunction) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
return sql.Collation_binary, 5
}

// Eval implements the Expression interface.
func (tt *DoltgresFunction) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
rowLen := len(row)
return int32(rowLen), nil
}

// Resolved implements the Expression interface.
func (tt *DoltgresFunction) Resolved() bool {
return true
}

// Children implements the Expression interface.
func (tt *DoltgresFunction) Children() []sql.Expression { return tt.args }

// IsNullable implements the Expression interface.
func (tt *DoltgresFunction) IsNullable() bool {
return false
}

// WithChildren implements the Expression interface.
func (*DoltgresFunction) WithChildren(children ...sql.Expression) (sql.Expression, error) {
return NewTemporaryTesting(children...)
}
9 changes: 9 additions & 0 deletions sql/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func NewFunction0(name string, fn func() Expression) Function0 {
}
}

// NewInstance implements the interface Function.
func (fn Function0) NewInstance(args []Expression) (Expression, error) {
if len(args) != 0 {
return nil, ErrInvalidArgumentNumber.New(fn.Name, 0, len(args))
Expand All @@ -116,6 +117,7 @@ func (fn Function0) NewInstance(args []Expression) (Expression, error) {
return fn.Fn(), nil
}

// NewInstance implements the interface Function.
func (fn Function1) NewInstance(args []Expression) (Expression, error) {
if len(args) != 1 {
return nil, ErrInvalidArgumentNumber.New(fn.Name, 1, len(args))
Expand All @@ -124,6 +126,7 @@ func (fn Function1) NewInstance(args []Expression) (Expression, error) {
return fn.Fn(args[0]), nil
}

// NewInstance implements the interface Function.
func (fn Function2) NewInstance(args []Expression) (Expression, error) {
if len(args) != 2 {
return nil, ErrInvalidArgumentNumber.New(fn.Name, 2, len(args))
Expand All @@ -132,6 +135,7 @@ func (fn Function2) NewInstance(args []Expression) (Expression, error) {
return fn.Fn(args[0], args[1]), nil
}

// NewInstance implements the interface Function.
func (fn Function3) NewInstance(args []Expression) (Expression, error) {
if len(args) != 3 {
return nil, ErrInvalidArgumentNumber.New(fn.Name, 3, len(args))
Expand All @@ -140,6 +144,7 @@ func (fn Function3) NewInstance(args []Expression) (Expression, error) {
return fn.Fn(args[0], args[1], args[2]), nil
}

// NewInstance implements the interface Function.
func (fn Function4) NewInstance(args []Expression) (Expression, error) {
if len(args) != 4 {
return nil, ErrInvalidArgumentNumber.New(fn.Name, 4, len(args))
Expand All @@ -148,6 +153,7 @@ func (fn Function4) NewInstance(args []Expression) (Expression, error) {
return fn.Fn(args[0], args[1], args[2], args[3]), nil
}

// NewInstance implements the interface Function.
func (fn Function5) NewInstance(args []Expression) (Expression, error) {
if len(args) != 5 {
return nil, ErrInvalidArgumentNumber.New(fn.Name, 5, len(args))
Expand All @@ -156,6 +162,7 @@ func (fn Function5) NewInstance(args []Expression) (Expression, error) {
return fn.Fn(args[0], args[1], args[2], args[3], args[4]), nil
}

// NewInstance implements the interface Function.
func (fn Function6) NewInstance(args []Expression) (Expression, error) {
if len(args) != 6 {
return nil, ErrInvalidArgumentNumber.New(fn.Name, 6, len(args))
Expand All @@ -164,6 +171,7 @@ func (fn Function6) NewInstance(args []Expression) (Expression, error) {
return fn.Fn(args[0], args[1], args[2], args[3], args[4], args[5]), nil
}

// NewInstance implements the interface Function.
func (fn Function7) NewInstance(args []Expression) (Expression, error) {
if len(args) != 7 {
return nil, ErrInvalidArgumentNumber.New(fn.Name, 7, len(args))
Expand All @@ -172,6 +180,7 @@ func (fn Function7) NewInstance(args []Expression) (Expression, error) {
return fn.Fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]), nil
}

// NewInstance implements the interface Function.
func (fn FunctionN) NewInstance(args []Expression) (Expression, error) {
return fn.Fn(args...)
}
Expand Down
1 change: 1 addition & 0 deletions sql/planbuilder/from.go
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ func (b *Builder) buildResolvedTable(inScope *scope, db, schema, name string, as
})
cols.Add(sql.ColumnId(id))
}
outScope.recordTableAsColumn(db, strings.ToLower(tab.Name()), tabId, rt)

rt = rt.WithId(tabId).WithColumns(cols).(*plan.ResolvedTable)
outScope.node = rt
Expand Down
39 changes: 32 additions & 7 deletions sql/planbuilder/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,44 @@ func (b *Builder) buildScalar(inScope *scope, e ast.Expr) (ex sql.Expression) {
return sysVar
}
}
var err error
if scope == ast.SetScope_User || scope == ast.SetScope_Persist || scope == ast.SetScope_PersistOnly {
err = sql.ErrUnknownUserVariable.New(colName)
err := sql.ErrUnknownUserVariable.New(colName)
b.handleErr(err)
} else if scope == ast.SetScope_Global || scope == ast.SetScope_Session {
err = sql.ErrUnknownSystemVariable.New(colName)
err := sql.ErrUnknownSystemVariable.New(colName)
b.handleErr(err)
} else if tblName != "" && !inScope.hasTable(tblName) {
err = sql.ErrTableNotFound.New(tblName)
err := sql.ErrTableNotFound.New(tblName)
b.handleErr(err)
} else if tblName != "" {
err = sql.ErrTableColumnNotFound.New(tblName, colName)
err := sql.ErrTableColumnNotFound.New(tblName, colName)
b.handleErr(err)
} else if inScope.hasTable(colName) {
// TODO: only relevant for Doltgres, this will use a hook
scopeTable, ok := inScope.resolveColumnAsTable(dbName, colName)
if !ok {
err := sql.ErrColumnNotFound.New(v)
b.handleErr(err)
}
tableSch := scopeTable.rt.Schema()
astQualifier := ast.TableName{
Name: ast.NewTableIdent(colName), // This must be the colName due to aliases
DbQualifier: ast.NewTableIdent(scopeTable.rt.Database().Name()),
}
fieldArgs := make([]sql.Expression, len(tableSch))
for i := range tableSch {
astArg := ast.ColName{
StoredProcVal: nil,
Qualifier: astQualifier,
Name: ast.NewColIdent(tableSch[i].Name),
}
fieldArgs[i] = b.buildScalar(inScope, &astArg)
}
return expression.NewDoltgresHookExpression(fieldArgs...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you want to use a hook as outlined in the other PR for this functionality. Those are limited to lifecycle events for schema objects, and I think you want to keep them limited to that. Throwing in planning is a really odd expansion of responsibility there that I don't think is warranted.

What you want here is for the builder object itself to have a ColumnResolver or similar, and the logic here can be totally opaque to the GMS code, implemented entirely in Doltgres. Realistically for now you would need to use function pointer swaps to get this integration, but in the longer term you could specify the details of planbuilder construction during engine initialization.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would need to export the Scope type for this to work.

} else {
err = sql.ErrColumnNotFound.New(v)
err := sql.ErrColumnNotFound.New(v)
b.handleErr(err)
}
b.handleErr(err)
}

origTbl := b.getOrigTblName(inScope.node, c.table)
Expand Down
Loading
Loading