From 60197e7e34e4d8b6170c108e61d8f5d97accd9e0 Mon Sep 17 00:00:00 2001 From: Peter Mattis Date: Wed, 23 Sep 2015 08:22:55 -0400 Subject: [PATCH] Expand subqueries in VALUES statements. Fixes #2621. --- sql/insert.go | 24 +++++++++++------- sql/testdata/subquery | 59 +++++++++++++++++++++++++++++++++++++++++++ sql/update.go | 23 ++++++++++++----- sql/values.go | 11 ++++++++ 4 files changed, 101 insertions(+), 16 deletions(-) diff --git a/sql/insert.go b/sql/insert.go index fd976a16a82e..791b06eb16b4 100644 --- a/sql/insert.go +++ b/sql/insert.go @@ -96,6 +96,8 @@ func (p *planner) Insert(n *parser.Insert) (planNode, error) { primaryIndex := tableDesc.PrimaryIndex primaryIndexKeyPrefix := MakeIndexKeyPrefix(tableDesc.ID, primaryIndex.ID) + marshalled := make([]interface{}, len(cols)) + var b client.Batch for rows.Next() { rowVals := rows.Values() @@ -124,6 +126,17 @@ func (p *planner) Insert(n *parser.Insert) (planNode, error) { } } + // Check that the row value types match the column types. This needs to + // happen before index encoding because certain datum types (i.e. tuple) + // cannot be used as index values. + for i, val := range rowVals { + // Make sure the value can be written to the column before proceeding. + var err error + if marshalled[i], err = marshalColumnValue(cols[i], val); err != nil { + return nil, err + } + } + primaryIndexKey, _, err := encodeIndexKey( primaryIndex.ColumnIDs, colIDtoRowIndex, rowVals, primaryIndexKeyPrefix) if err != nil { @@ -153,13 +166,6 @@ func (p *planner) Insert(n *parser.Insert) (planNode, error) { // Write the row columns. for i, val := range rowVals { col := cols[i] - - // Make sure the value can be written to the column before proceeding. - marshalled, err := marshalColumnValue(col, val) - if err != nil { - return nil, err - } - if _, ok := primaryKeyCols[col.ID]; ok { // Skip primary key columns as their values are encoded in the row // sentinel key which is guaranteed to exist for as long as the row @@ -167,7 +173,7 @@ func (p *planner) Insert(n *parser.Insert) (planNode, error) { continue } - if marshalled != nil { + if marshalled[i] != nil { // We only output non-NULL values. Non-existent column keys are // considered NULL during scanning and the row sentinel ensures we know // the row exists. @@ -177,7 +183,7 @@ func (p *planner) Insert(n *parser.Insert) (planNode, error) { log.Infof("CPut %q -> %v", key, val) } - b.CPut(key, marshalled, nil) + b.CPut(key, marshalled[i], nil) } } } diff --git a/sql/testdata/subquery b/sql/testdata/subquery index 773197bed97e..9cc5c793edde 100644 --- a/sql/testdata/subquery +++ b/sql/testdata/subquery @@ -61,3 +61,62 @@ query I SELECT (SELECT a FROM abc WHERE false) ---- NULL + +query I +VALUES (1, (SELECT (2))) +---- +1 2 + +statement ok +INSERT INTO abc VALUES ((SELECT 7), (SELECT 8), (SELECT 9)) + +query III +SELECT * FROM abc WHERE a = 7 +---- +7 8 9 + +statement error value type tuple doesn't match type INT of column "a" +INSERT INTO abc VALUES ((SELECT (10, 11, 12))) + +statement error subquery must return only one column, found 3 +INSERT INTO abc VALUES ((SELECT 10, 11, 12)) + +statement ok +CREATE TABLE xyz (x INT PRIMARY KEY, y INT, z INT) + +statement ok +INSERT INTO xyz SELECT * FROM abc + +query III +SELECT * FROM xyz +---- +1 2 3 +4 5 6 +7 8 9 + +statement ok +UPDATE xyz SET z = (SELECT 10) WHERE x = 7 + +query III +SELECT * FROM xyz +---- +1 2 3 +4 5 6 +7 8 10 + +statement error value type tuple doesn't match type INT of column "z" +UPDATE xyz SET z = (SELECT (10, 11)) WHERE x = 7 + +statement error subquery must return 2 columns, found 1 +UPDATE xyz SET (y, z) = (SELECT (11, 12)) WHERE x = 7 + +statement ok +UPDATE xyz SET (y, z) = (SELECT 11, 12) WHERE x = 7 + +query III +SELECT * FROM xyz +---- +1 2 3 +4 5 6 +7 11 12 + diff --git a/sql/update.go b/sql/update.go index 8ad1060ad554..16b74b318c1b 100644 --- a/sql/update.go +++ b/sql/update.go @@ -157,6 +157,8 @@ func (p *planner) Update(n *parser.Update) (planNode, error) { } } + marshalled := make([]interface{}, len(cols)) + // Update all the rows. var b client.Batch for rows.Next() { @@ -184,12 +186,24 @@ func (p *planner) Update(n *parser.Update) (planNode, error) { } rowVals[colIDtoRowIndex[col.ID]] = val } + + // Check that the new value types match the column types. This needs to + // happen before index encoding because certain datum types (i.e. tuple) + // cannot be used as index values. + for i, val := range newVals { + var err error + if marshalled[i], err = marshalColumnValue(cols[i], val); err != nil { + return nil, err + } + } + // Compute the new secondary index key:value pairs for this row. newSecondaryIndexEntries, err := encodeSecondaryIndexes( tableDesc.ID, indexes, colIDtoRowIndex, rowVals) if err != nil { return nil, err } + // Update secondary indexes. for i, newSecondaryIndexEntry := range newSecondaryIndexEntries { secondaryIndexEntry := secondaryIndexEntries[i] @@ -209,13 +223,8 @@ func (p *planner) Update(n *parser.Update) (planNode, error) { for i, val := range newVals { col := cols[i] - marshalled, err := marshalColumnValue(col, val) - if err != nil { - return nil, err - } - key := MakeColumnKey(col.ID, primaryIndexKey) - if marshalled != nil { + if marshalled[i] != nil { // We only output non-NULL values. Non-existent column keys are // considered NULL during scanning and the row sentinel ensures we know // the row exists. @@ -223,7 +232,7 @@ func (p *planner) Update(n *parser.Update) (planNode, error) { log.Infof("Put %q -> %v", key, val) } - b.Put(key, marshalled) + b.Put(key, marshalled[i]) } else { // The column might have already existed but is being set to NULL, so // delete it. diff --git a/sql/values.go b/sql/values.go index deb7fbff7b1c..63b814d2ff7a 100644 --- a/sql/values.go +++ b/sql/values.go @@ -32,6 +32,17 @@ func (p *planner) Values(n parser.Values) (planNode, error) { nCols := 0 for _, tuple := range n { + for i := range tuple { + var err error + tuple[i], err = p.evalCtx.NormalizeAndTypeCheckExpr(tuple[i]) + if err != nil { + return nil, err + } + tuple[i], err = p.expandSubqueries(tuple[i], 1) + if err != nil { + return nil, err + } + } data, err := p.evalCtx.EvalExpr(tuple) if err != nil { return nil, err