Skip to content

Commit 9f7b38e

Browse files
committed
Merge branch 'main' of github.com:dolthub/go-mysql-server into elianddb/9325-support-oct-function
2 parents af645db + c62c7e3 commit 9f7b38e

File tree

11 files changed

+134
-74
lines changed

11 files changed

+134
-74
lines changed

enginetest/memory_engine_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ func TestSingleScript(t *testing.T) {
226226

227227
for _, test := range scripts {
228228
harness := enginetest.NewMemoryHarness("", 1, testNumPartitions, true, nil)
229-
harness.UseServer()
229+
//harness.UseServer()
230230
engine, err := harness.NewEngine(t)
231231
if err != nil {
232232
panic(err)

enginetest/queries/insert_queries.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2301,9 +2301,13 @@ var InsertScripts = []ScriptTest{
23012301
Expected: []sql.Row{{1, "Cat"}},
23022302
},
23032303
{
2304-
Query: "insert into auto_pk values (NULL, 'Dog'),(5, 'Fish'),(NULL, 'Horse') returning pk,name",
2304+
Query: "insert into auto_pk values (NULL, 'Dog'),(5, 'Fish'),(NULL, 'Horse') returning *",
23052305
Expected: []sql.Row{{2, "Dog"}, {5, "Fish"}, {6, "Horse"}},
23062306
},
2307+
{
2308+
Query: "insert into auto_pk (name) select name from animals where id = 3 returning *",
2309+
Expected: []sql.Row{{7, "Tiger"}},
2310+
},
23072311
},
23082312
},
23092313
}

enginetest/queries/script_queries.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8684,6 +8684,34 @@ where
86848684
},
86858685
},
86868686
},
8687+
{
8688+
Name: "substring function tests with wrappers",
8689+
Dialect: "mysql",
8690+
SetUpScript: []string{
8691+
"create table tbl (t text);",
8692+
"insert into tbl values ('abcdef');",
8693+
},
8694+
Assertions: []ScriptTestAssertion{
8695+
{
8696+
Query: "select left(t, 3) from tbl;",
8697+
Expected: []sql.Row{
8698+
{"abc"},
8699+
},
8700+
},
8701+
{
8702+
Query: "select right(t, 3) from tbl;",
8703+
Expected: []sql.Row{
8704+
{"def"},
8705+
},
8706+
},
8707+
{
8708+
Query: "select instr(t, 'bcd') from tbl;",
8709+
Expected: []sql.Row{
8710+
{2},
8711+
},
8712+
},
8713+
},
8714+
},
86878715
}
86888716

86898717
var SpatialScriptTests = []ScriptTest{

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/dolthub/go-icu-regex v0.0.0-20250327004329-6799764f2dad
77
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71
88
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81
9-
github.com/dolthub/vitess v0.0.0-20250605180032-fa2a634c215b
9+
github.com/dolthub/vitess v0.0.0-20250609213846-75541d7ef20a
1010
github.com/go-kit/kit v0.10.0
1111
github.com/go-sql-driver/mysql v1.7.2-0.20231213112541-0004702b931d
1212
github.com/gocraft/dbr/v2 v2.7.2

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 h1:7/v8q9X
6060
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY=
6161
github.com/dolthub/vitess v0.0.0-20250605180032-fa2a634c215b h1:rgZXgRYZ3SZbb4Tz5Y6vnzvB7P9pFvEP+Q7UGfRC9uY=
6262
github.com/dolthub/vitess v0.0.0-20250605180032-fa2a634c215b/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
63+
github.com/dolthub/vitess v0.0.0-20250609213846-75541d7ef20a h1:DWQt6KSgrkZYuxzvGflImldau0a3IfINhEGQnFst/pw=
64+
github.com/dolthub/vitess v0.0.0-20250609213846-75541d7ef20a/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
6365
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
6466
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
6567
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=

sql/analyzer/apply_foreign_keys.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ func applyForeignKeysToNodes(ctx *sql.Context, a *Analyzer, n sql.Node, cache *f
122122
if plan.IsEmptyTable(n.Child) {
123123
return n, transform.SameTree, nil
124124
}
125+
// TODO: UPDATE JOIN can update multiple tables. Because updatableJoinTable does not implement
126+
// sql.ForeignKeyTable, we do not currenly support FK checks for UPDATE JOIN statements.
125127
updateDest, err := plan.GetUpdatable(n.Child)
126128
if err != nil {
127129
return nil, transform.SameTree, err

sql/expression/function/substring.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,20 @@ func (l Left) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
349349
switch str := str.(type) {
350350
case string:
351351
text = []rune(str)
352+
case sql.StringWrapper:
353+
s, err := str.Unwrap(ctx)
354+
if err != nil {
355+
return nil, err
356+
}
357+
text = []rune(s)
352358
case []byte:
353359
text = []rune(string(str))
360+
case sql.BytesWrapper:
361+
b, err := str.Unwrap(ctx)
362+
if err != nil {
363+
return nil, err
364+
}
365+
text = []rune(string(b))
354366
case nil:
355367
return nil, nil
356368
default:
@@ -583,8 +595,20 @@ func (i Instr) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
583595
switch str := str.(type) {
584596
case string:
585597
text = []rune(str)
598+
case sql.StringWrapper:
599+
s, err := str.Unwrap(ctx)
600+
if err != nil {
601+
return nil, err
602+
}
603+
text = []rune(s)
586604
case []byte:
587605
text = []rune(string(str))
606+
case sql.BytesWrapper:
607+
s, err := str.Unwrap(ctx)
608+
if err != nil {
609+
return nil, err
610+
}
611+
text = []rune(string(s))
588612
case nil:
589613
return nil, nil
590614
default:
@@ -600,8 +624,20 @@ func (i Instr) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
600624
switch substr := substr.(type) {
601625
case string:
602626
subtext = []rune(substr)
627+
case sql.StringWrapper:
628+
s, err := substr.Unwrap(ctx)
629+
if err != nil {
630+
return nil, err
631+
}
632+
text = []rune(s)
603633
case []byte:
604-
subtext = []rune(string(subtext))
634+
subtext = []rune(string(substr))
635+
case sql.BytesWrapper:
636+
s, err := substr.Unwrap(ctx)
637+
if err != nil {
638+
return nil, err
639+
}
640+
subtext = []rune(string(s))
605641
case nil:
606642
return nil, nil
607643
default:

sql/plan/update_join.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ func (u *UpdateJoin) DebugString() string {
5454

5555
// GetUpdatable returns an updateJoinTable which implements sql.UpdatableTable.
5656
func (u *UpdateJoin) GetUpdatable() sql.UpdatableTable {
57+
// TODO: UpdateJoin can update multiple tables, but this interface only allows for a single table.
58+
// Additionally, updatableJoinTable doesn't implement interfaces that other parts of the code
59+
// expect, so UpdateJoins don't always work correctly. For example, because updatableJoinTable
60+
// doesn't implement ForeignKeyTable, UpdateJoin statements don't enforce foreign key checks.
61+
// We should revamp this function so that we can communicate multiple tables being updated.
5762
return &updatableJoinTable{
5863
updaters: u.Updaters,
5964
joinNode: u.Child.(*UpdateSource).Child,

sql/planbuilder/dml.go

Lines changed: 44 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,10 @@ func (b *Builder) buildInsert(inScope *scope, i *ast.Insert) (outScope *scope) {
150150
ins := plan.NewInsertInto(db, plan.NewInsertDestination(sch, dest), srcScope.node, isReplace, columns, onDupExprs, ignore)
151151
ins.LiteralValueSource = srcLiteralOnly
152152

153-
if i.Returning != nil {
154-
returningExprs := make([]sql.Expression, len(i.Returning))
155-
for i, selectExpr := range i.Returning {
156-
returningExprs[i] = b.selectExprToExpression(destScope, selectExpr)
157-
}
158-
ins.Returning = returningExprs
153+
if len(i.Returning) > 0 {
154+
// TODO: read returning results from outScope instead of ins.Returning so that there is no need to return list
155+
// of expressions
156+
ins.Returning = b.analyzeSelectList(destScope, destScope, i.Returning)
159157
}
160158

161159
b.validateInsert(ins)
@@ -492,6 +490,11 @@ func (b *Builder) buildDelete(inScope *scope, d *ast.Delete) (outScope *scope) {
492490
return
493491
}
494492

493+
// buildUpdate builds a Update node from |u|. If the update joins tables, the returned Update node's
494+
// children will have a JoinNode, which will later be replaced by an UpdateJoin node during analysis. We
495+
// don't create the UpdateJoin node here, because some query plans, such as IN SUBQUERY nodes, require
496+
// analyzer processing that converts the subquery into a join, and then requires the same logic to
497+
// create an UpdateJoin node under the original Update node.
495498
func (b *Builder) buildUpdate(inScope *scope, u *ast.Update) (outScope *scope) {
496499
// TODO: this shouldn't be called during ComPrepare or `PREPARE ... FROM ...` statements, but currently it is.
497500
// The end result is that the ComDelete counter is incremented during prepare statements, which is incorrect.
@@ -534,44 +537,26 @@ func (b *Builder) buildUpdate(inScope *scope, u *ast.Update) (outScope *scope) {
534537
update.IsProcNested = b.ProcCtx().DbName != ""
535538

536539
var checks []*sql.CheckConstraint
537-
if join, ok := outScope.node.(*plan.JoinNode); ok {
538-
// TODO this doesn't work, a lot of the time the top node
539-
// is a filter. This would have to go before we build the
540-
// filter/accessory nodes. But that errors for a lot of queries.
541-
source := plan.NewUpdateSource(
542-
join,
543-
ignore,
544-
updateExprs,
545-
)
546-
updaters, err := rowUpdatersByTable(b.ctx, source, join)
540+
if hasJoinNode(outScope.node) {
541+
tablesToUpdate, err := getResolvedTablesToUpdate(b.ctx, update.Child, outScope.node)
547542
if err != nil {
548543
b.handleErr(err)
549544
}
550-
updateJoin := plan.NewUpdateJoin(updaters, source)
551-
update.Child = updateJoin
552-
transform.Inspect(update, func(n sql.Node) bool {
553-
// todo maybe this should be later stage
554-
switch n := n.(type) {
555-
case sql.NameableNode:
556-
if _, ok := updaters[n.Name()]; ok {
557-
rt := getResolvedTable(n)
558-
tableScope := inScope.push()
559-
for _, c := range rt.Schema() {
560-
tableScope.addColumn(scopeColumn{
561-
db: rt.SqlDatabase.Name(),
562-
table: strings.ToLower(n.Name()),
563-
tableId: tableScope.tables[strings.ToLower(n.Name())],
564-
col: strings.ToLower(c.Name),
565-
typ: c.Type,
566-
nullable: c.Nullable,
567-
})
568-
}
569-
checks = append(checks, b.loadChecksFromTable(tableScope, rt.Table)...)
570-
}
571-
default:
545+
546+
for _, rt := range tablesToUpdate {
547+
tableScope := inScope.push()
548+
for _, c := range rt.Schema() {
549+
tableScope.addColumn(scopeColumn{
550+
db: rt.SqlDatabase.Name(),
551+
table: strings.ToLower(rt.Name()),
552+
tableId: tableScope.tables[strings.ToLower(rt.Name())],
553+
col: strings.ToLower(c.Name),
554+
typ: c.Type,
555+
nullable: c.Nullable,
556+
})
572557
}
573-
return true
574-
})
558+
checks = append(checks, b.loadChecksFromTable(tableScope, rt.Table)...)
559+
}
575560
} else {
576561
transform.Inspect(update, func(n sql.Node) bool {
577562
// todo maybe this should be later stage
@@ -583,46 +568,39 @@ func (b *Builder) buildUpdate(inScope *scope, u *ast.Update) (outScope *scope) {
583568
}
584569

585570
if len(u.Returning) > 0 {
586-
returningExprs := make([]sql.Expression, len(u.Returning))
587-
for i, selectExpr := range u.Returning {
588-
returningExprs[i] = b.selectExprToExpression(outScope, selectExpr)
589-
}
590-
update.Returning = returningExprs
571+
update.Returning = b.analyzeSelectList(outScope, outScope, u.Returning)
591572
}
592573

593574
outScope.node = update.WithChecks(checks)
594575
return
595576
}
596577

597-
// rowUpdatersByTable maps a set of tables to their RowUpdater objects.
598-
func rowUpdatersByTable(ctx *sql.Context, node sql.Node, ij sql.Node) (map[string]sql.RowUpdater, error) {
599-
namesOfTableToBeUpdated := getTablesToBeUpdated(node)
600-
resolvedTables := getTablesByName(ij)
601-
602-
rowUpdatersByTable := make(map[string]sql.RowUpdater)
603-
for tableToBeUpdated, _ := range namesOfTableToBeUpdated {
604-
resolvedTable, ok := resolvedTables[strings.ToLower(tableToBeUpdated)]
605-
if !ok {
606-
return nil, plan.ErrUpdateForTableNotSupported.New(tableToBeUpdated)
578+
// hasJoinNode returns true if |node| or any child is a JoinNode.
579+
func hasJoinNode(node sql.Node) bool {
580+
updateJoinFound := false
581+
transform.Inspect(node, func(n sql.Node) bool {
582+
if _, ok := n.(*plan.JoinNode); ok {
583+
updateJoinFound = true
607584
}
585+
return !updateJoinFound
586+
})
587+
return updateJoinFound
588+
}
608589

609-
var table = resolvedTable.UnderlyingTable()
590+
func getResolvedTablesToUpdate(_ *sql.Context, node sql.Node, ij sql.Node) (resolvedTables []*plan.ResolvedTable, err error) {
591+
namesOfTablesToBeUpdated := getTablesToBeUpdated(node)
592+
resolvedTablesMap := getTablesByName(ij)
610593

611-
// If there is no UpdatableTable for a table being updated, error out
612-
updatable, ok := table.(sql.UpdatableTable)
613-
if !ok && updatable == nil {
594+
for tableToBeUpdated, _ := range namesOfTablesToBeUpdated {
595+
resolvedTable, ok := resolvedTablesMap[strings.ToLower(tableToBeUpdated)]
596+
if !ok {
614597
return nil, plan.ErrUpdateForTableNotSupported.New(tableToBeUpdated)
615598
}
616599

617-
keyless := sql.IsKeyless(updatable.Schema())
618-
if keyless {
619-
return nil, sql.ErrUnsupportedFeature.New("error: keyless tables unsupported for UPDATE JOIN")
620-
}
621-
622-
rowUpdatersByTable[tableToBeUpdated] = updatable.Updater(ctx)
600+
resolvedTables = append(resolvedTables, resolvedTable)
623601
}
624602

625-
return rowUpdatersByTable, nil
603+
return resolvedTables, nil
626604
}
627605

628606
// getTablesByName takes a node and returns all found resolved tables in a map.

sql/planbuilder/project.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ func (b *Builder) analyzeProjectionList(inScope, outScope *scope, selectExprs as
2929
b.analyzeSelectList(inScope, outScope, selectExprs)
3030
}
3131

32-
func (b *Builder) analyzeSelectList(inScope, outScope *scope, selectExprs ast.SelectExprs) {
33-
// todo ideally we would not create new expressions here.
32+
func (b *Builder) analyzeSelectList(inScope, outScope *scope, selectExprs ast.SelectExprs) (expressions []sql.Expression) {
33+
// TODO: ideally we would not create new expressions here.
3434
// we want to in-place identify aggregations, expand stars.
3535
// use inScope to construct projections for projScope
3636

@@ -160,6 +160,7 @@ func (b *Builder) analyzeSelectList(inScope, outScope *scope, selectExprs ast.Se
160160
}
161161

162162
inScope.parent = tempScope.parent
163+
return exprs
163164
}
164165

165166
// selectExprToExpression binds dependencies in a scalar expression in a SELECT clause.

0 commit comments

Comments
 (0)