Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.

Commit c2de021

Browse files
Document and test VIEW-related changes
Signed-off-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
1 parent 032fa6c commit c2de021

File tree

9 files changed

+799
-99
lines changed

9 files changed

+799
-99
lines changed

sql/analyzer/resolve_tables_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ func TestResolveTablesNested(t *testing.T) {
9292
require.Equal(expected, analyzed)
9393
}
9494

95+
// Tests the resolution of views (ensuring it is case-insensitive), that should
96+
// result in the replacement of the UnresolvedTable with the SubqueryAlias that
97+
// represents the view
9598
func TestResolveViews(t *testing.T) {
9699
require := require.New(t)
97100

@@ -112,22 +115,26 @@ func TestResolveViews(t *testing.T) {
112115
subqueryAlias := plan.NewSubqueryAlias("myview", subquery)
113116
view := sql.NewView("myview", subqueryAlias)
114117

118+
// Register the view in the catalog
115119
catalog := sql.NewCatalog()
116120
catalog.AddDatabase(db)
117121
catalog.ViewRegistry.Register(db.Name(), view)
118122

119123
a := NewBuilder(catalog).AddPostAnalyzeRule(f.Name, f.Apply).Build()
120124

125+
// Check whether the view is resolved and replaced with the subquery
121126
var notAnalyzed sql.Node = plan.NewUnresolvedTable("myview", "")
122127
analyzed, err := f.Apply(sql.NewEmptyContext(), a, notAnalyzed)
123128
require.NoError(err)
124129
require.Equal(subqueryAlias, analyzed)
125130

131+
// Ensures that the resolution is case-insensitive
126132
notAnalyzed = plan.NewUnresolvedTable("MyVieW", "")
127133
analyzed, err = f.Apply(sql.NewEmptyContext(), a, notAnalyzed)
128134
require.NoError(err)
129135
require.Equal(subqueryAlias, analyzed)
130136

137+
// Ensures that the resolution is idempotent
131138
analyzed, err = f.Apply(sql.NewEmptyContext(), a, subqueryAlias)
132139
require.NoError(err)
133140
require.Equal(subqueryAlias, analyzed)

sql/parse/parse_test.go

Lines changed: 20 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package parse
22

33
import (
4-
"testing"
54
"math"
5+
"testing"
66

77
"github.com/src-d/go-mysql-server/sql/expression"
88
"github.com/src-d/go-mysql-server/sql/expression/function/aggregation"
@@ -55,44 +55,44 @@ var fixtures = map[string]sql.Node{
5555
sql.UnresolvedDatabase(""),
5656
"t1",
5757
sql.Schema{{
58-
Name: "a",
59-
Type: sql.Int32,
60-
Nullable: false,
58+
Name: "a",
59+
Type: sql.Int32,
60+
Nullable: false,
6161
PrimaryKey: true,
6262
}, {
63-
Name: "b",
64-
Type: sql.Text,
65-
Nullable: true,
63+
Name: "b",
64+
Type: sql.Text,
65+
Nullable: true,
6666
PrimaryKey: false,
6767
}},
6868
),
6969
`CREATE TABLE t1(a INTEGER, b TEXT, PRIMARY KEY (a))`: plan.NewCreateTable(
7070
sql.UnresolvedDatabase(""),
7171
"t1",
7272
sql.Schema{{
73-
Name: "a",
74-
Type: sql.Int32,
75-
Nullable: true,
73+
Name: "a",
74+
Type: sql.Int32,
75+
Nullable: true,
7676
PrimaryKey: true,
7777
}, {
78-
Name: "b",
79-
Type: sql.Text,
80-
Nullable: true,
78+
Name: "b",
79+
Type: sql.Text,
80+
Nullable: true,
8181
PrimaryKey: false,
8282
}},
8383
),
8484
`CREATE TABLE t1(a INTEGER, b TEXT, PRIMARY KEY (a, b))`: plan.NewCreateTable(
8585
sql.UnresolvedDatabase(""),
8686
"t1",
8787
sql.Schema{{
88-
Name: "a",
89-
Type: sql.Int32,
90-
Nullable: true,
88+
Name: "a",
89+
Type: sql.Int32,
90+
Nullable: true,
9191
PrimaryKey: true,
9292
}, {
93-
Name: "b",
94-
Type: sql.Text,
95-
Nullable: true,
93+
Name: "b",
94+
Type: sql.Text,
95+
Nullable: true,
9696
PrimaryKey: true,
9797
}},
9898
),
@@ -1263,42 +1263,6 @@ var fixtures = map[string]sql.Node{
12631263
},
12641264
plan.NewUnresolvedTable("dual", ""),
12651265
),
1266-
`CREATE VIEW myview AS SELECT 1`: plan.NewCreateView(
1267-
sql.UnresolvedDatabase(""),
1268-
"myview",
1269-
nil,
1270-
plan.NewSubqueryAlias("myview",
1271-
plan.NewProject(
1272-
[]sql.Expression{expression.NewLiteral(int8(1), sql.Int8)},
1273-
plan.NewUnresolvedTable("dual", ""),
1274-
),
1275-
),
1276-
false,
1277-
),
1278-
`CREATE VIEW mydb.myview AS SELECT 1`: plan.NewCreateView(
1279-
sql.UnresolvedDatabase("mydb"),
1280-
"myview",
1281-
nil,
1282-
plan.NewSubqueryAlias("myview",
1283-
plan.NewProject(
1284-
[]sql.Expression{expression.NewLiteral(int8(1), sql.Int8)},
1285-
plan.NewUnresolvedTable("dual", ""),
1286-
),
1287-
),
1288-
false,
1289-
),
1290-
`CREATE OR REPLACE VIEW mydb.myview AS SELECT 1`: plan.NewCreateView(
1291-
sql.UnresolvedDatabase("mydb"),
1292-
"myview",
1293-
nil,
1294-
plan.NewSubqueryAlias( "myview",
1295-
plan.NewProject(
1296-
[]sql.Expression{expression.NewLiteral(int8(1), sql.Int8)},
1297-
plan.NewUnresolvedTable("dual", ""),
1298-
),
1299-
),
1300-
true,
1301-
),
13021266
}
13031267

13041268
func TestParse(t *testing.T) {
@@ -1332,7 +1296,7 @@ var fixturesErrors = map[string]*errors.Kind{
13321296
`SELECT INTERVAL 1 DAY + INTERVAL 1 DAY`: ErrUnsupportedSyntax,
13331297
`SELECT '2018-05-01' + (INTERVAL 1 DAY + INTERVAL 1 DAY)`: ErrUnsupportedSyntax,
13341298
`SELECT AVG(DISTINCT foo) FROM b`: ErrUnsupportedSyntax,
1335-
`CREATE VIEW myview (col1) AS SELECT 1`: ErrUnsupportedSyntax,
1299+
`CREATE VIEW myview (col1) AS SELECT 1`: ErrUnsupportedSyntax,
13361300
}
13371301

13381302
func TestParseErrors(t *testing.T) {

sql/parse/util.go

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,27 @@ func skipSpaces(r *bufio.Reader) error {
8080
}
8181
}
8282

83+
// Reads every contiguous space from the reader, populating numSpacesRead with
84+
// the number of spaces read.
85+
func readSpaces(r *bufio.Reader, numSpacesRead *int) error {
86+
*numSpacesRead = 0
87+
for {
88+
ru, _, err := r.ReadRune()
89+
if err == io.EOF {
90+
return nil
91+
}
92+
93+
if err != nil {
94+
return err
95+
}
96+
97+
if !unicode.IsSpace(ru) {
98+
return r.UnreadRune()
99+
}
100+
*numSpacesRead++
101+
}
102+
}
103+
83104
func checkEOF(rd *bufio.Reader) error {
84105
r, _, err := rd.ReadRune()
85106
if err == io.EOF {
@@ -241,7 +262,6 @@ func readIdent(ident *string) parseFunc {
241262
}
242263
}
243264

244-
245265
// Reads a scoped identifier, populating the specified slice with the different
246266
// parts of the identifier if it is correctly formed.
247267
// A scoped identifier is a sequence of identifiers separated by the specified
@@ -264,10 +284,12 @@ func readScopedIdent(separator rune, idents *[]string) parseFunc {
264284
}
265285
}
266286

267-
*idents = append(
268-
*idents,
269-
strings.Split(strings.ToLower(buf.String()), string(separator))...,
270-
)
287+
if readString := buf.String(); len(readString) > 0 {
288+
*idents = append(
289+
*idents,
290+
strings.Split(strings.ToLower(readString), string(separator))...,
291+
)
292+
}
271293
return nil
272294
}
273295
}
@@ -414,32 +436,32 @@ func maybe(matched *bool, str string) parseFunc {
414436
}
415437
}
416438

417-
// Tries to read the specified strings, one after the other, separeted by an
418-
// arbitrary number of spaces. It consumes the reader if and only all the
439+
// Tries to read the specified strings, one after the other, separated by an
440+
// arbitrary number of spaces. It consumes the reader if and only if all the
419441
// strings are found.
420442
func multiMaybe(matched *bool, strings ...string) parseFunc {
421443
return func(rd *bufio.Reader) error {
422444
*matched = false
423-
first := true
445+
var read string
424446
for _, str := range strings {
425447
if err := maybe(matched, str)(rd); err != nil {
426448
return err
427449
}
428450

429451
if !*matched {
430-
if first {
431-
return nil
432-
}
433-
434-
// TODO: add actual string parsed
435-
return errUnexpectedSyntax.New(str, "smth else")
452+
unreadString(rd, read)
453+
return nil
436454
}
437455

438-
first = false
439-
440-
if err := skipSpaces(rd); err != nil {
456+
var numSpaces int
457+
if err := readSpaces(rd, &numSpaces); err != nil {
441458
return err
442459
}
460+
461+
read = read + str
462+
for i := 0; i < numSpaces; i++ {
463+
read = read + " "
464+
}
443465
}
444466
*matched = true
445467
return nil
@@ -450,7 +472,9 @@ func multiMaybe(matched *bool, strings ...string) parseFunc {
450472
// indicating the opening of the list and another one specifying its closing.
451473
// For example, readList('(', ',', ')', list) parses "(uno, dos,tres)" and
452474
// populates list with the array of strings ["uno", "dos", "tres"]
453-
// If the opening is not found, this does not consumes any rune from the reader.
475+
// If the opening is not found, this does not consumes any rune from the
476+
// reader. If there is a parsing error after some elements were found, the list
477+
// is partially populated with the correct fields
454478
func maybeList(opening, separator, closing rune, list *[]string) parseFunc {
455479
return func(rd *bufio.Reader) error {
456480
r, _, err := rd.ReadRune()

0 commit comments

Comments
 (0)