Skip to content
This repository was archived by the owner on Feb 21, 2024. It is now read-only.

Commit 3f7ae75

Browse files
committed
increase parser test coverage significantly
We now test the tuple-assignment at all, although it's perhaps confusing because we expect a ()-list of columns to go with a {}-list of values. We also test a lot more errors and some more successes, and additional literal types in mustParseLiteral.
1 parent d4fb807 commit 3f7ae75

4 files changed

Lines changed: 103 additions & 9 deletions

File tree

sql3/parser/ast.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ func CloneExpr(expr Expr) Expr {
299299
return expr.Clone()
300300
case *StringLit:
301301
return expr.Clone()
302+
case *TupleLiteralExpr:
303+
return expr.Clone()
302304
case *UnaryExpr:
303305
return expr.Clone()
304306
case *Variable:
@@ -1795,6 +1797,7 @@ func (t *Type) String() string {
17951797
type StringLit struct {
17961798
ValuePos Pos // literal position
17971799
Value string // literal value (without quotes)
1800+
IsBlob bool // are we a blob?
17981801
}
17991802

18001803
func (expr *StringLit) IsLiteral() bool { return true }
@@ -1831,7 +1834,11 @@ func (lit *StringLit) Clone() *StringLit {
18311834

18321835
// String returns the string representation of the expression.
18331836
func (lit *StringLit) String() string {
1834-
return `'` + strings.Replace(lit.Value, `'`, `''`, -1) + `'`
1837+
if lit.IsBlob {
1838+
return `x'` + strings.Replace(lit.Value, `'`, `''`, -1) + `'`
1839+
} else {
1840+
return `'` + strings.Replace(lit.Value, `'`, `''`, -1) + `'`
1841+
}
18351842
}
18361843

18371844
type IntegerLit struct {

sql3/parser/parser.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,9 @@ func (p *Parser) parseCreateDatabaseStatement(createPos Pos) (_ *CreateDatabaseS
414414
if stmt.Options, err = p.parseDatabaseOptions(); err != nil {
415415
return &stmt, err
416416
}
417+
if len(stmt.Options) == 0 {
418+
return &stmt, p.errorExpected(stmt.With, p.peek(), "at least one option after WITH")
419+
}
417420
}
418421

419422
return &stmt, nil
@@ -443,19 +446,17 @@ func (p *Parser) parseDatabaseOptions() (_ []DatabaseOption, err error) {
443446
func (p *Parser) parseDatabaseOption() (_ DatabaseOption, err error) {
444447
assert(isDatabaseOptionStartToken(p.peek()))
445448

446-
var optionPos Pos
447-
448449
// Parse database options.
449450
switch p.peek() {
450451
case UNITS:
451-
return p.parseUnitsOption(optionPos)
452+
return p.parseUnitsOption()
452453
default:
453454
assert(p.peek() == COMMENT)
454-
return p.parseCommentOption(optionPos)
455+
return p.parseCommentOption()
455456
}
456457
}
457458

458-
func (p *Parser) parseUnitsOption(optionPos Pos) (_ *UnitsOption, err error) {
459+
func (p *Parser) parseUnitsOption() (_ *UnitsOption, err error) {
459460
assert(p.peek() == UNITS)
460461

461462
var opt UnitsOption
@@ -562,11 +563,11 @@ func (p *Parser) parseTableOption() (_ TableOption, err error) {
562563
return p.parseKeyPartitionsOption(optionPos)
563564
default:
564565
assert(p.peek() == COMMENT)
565-
return p.parseCommentOption(optionPos)
566+
return p.parseCommentOption()
566567
}
567568
}
568569

569-
func (p *Parser) parseCommentOption(optionPos Pos) (_ *CommentOption, err error) {
570+
func (p *Parser) parseCommentOption() (_ *CommentOption, err error) {
570571
assert(p.peek() == COMMENT)
571572

572573
var opt CommentOption
@@ -2731,7 +2732,7 @@ func (p *Parser) mustParseLiteral() Expr {
27312732
case TRUE, FALSE:
27322733
return &BoolLit{ValuePos: pos, Value: tok == TRUE}
27332734
case BLOB:
2734-
return &StringLit{ValuePos: pos, Value: lit}
2735+
return &StringLit{ValuePos: pos, IsBlob: true, Value: lit}
27352736
default:
27362737
assert(tok == NULL)
27372738
return &NullLit{ValuePos: pos}

sql3/parser/parser_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,11 @@ func TestParser_ParseAlterStatement(t *testing.T) {
460460
AssertParseStatementError(t, `ALTER TABLE tbl ADD`, `1:19: expected COLUMN keyword or column name, found 'EOF'`)
461461
AssertParseStatementError(t, `ALTER TABLE tbl ADD COLUMN`, `1:26: expected column name, found 'EOF'`)
462462
})
463+
t.Run("AlterView", func(t *testing.T) {
464+
AssertParseStatementError(t, `ALTER VIEW`, `1:10: expected view name, found 'EOF'`)
465+
AssertParseStatementError(t, `ALTER VIEW vw 23`, `1:15: expected AS, found 23`)
466+
AssertParseStatementError(t, `ALTER VIEW vw AS 23`, `1:18: expected SELECT, found 23`)
467+
})
463468
}
464469

465470
func TestParser_ParseFunctionStatement(t *testing.T) {
@@ -900,11 +905,63 @@ func TestParser_ParseStatement(t *testing.T) {
900905
},
901906
},
902907
})
908+
AssertParseStatement(t, `CREATE DATABASE db WITH COMMENT 'foo' COMMENT 23.5 COMMENT true COMMENT x'foo' COMMENT NULL`, &parser.CreateDatabaseStatement{
909+
Create: pos(0),
910+
Database: pos(7),
911+
Name: &parser.Ident{
912+
Name: "db",
913+
NamePos: pos(16),
914+
},
915+
With: pos(19),
916+
Options: []parser.DatabaseOption{
917+
&parser.CommentOption{
918+
Comment: pos(24),
919+
Expr: &parser.StringLit{
920+
ValuePos: pos(32),
921+
Value: "foo",
922+
},
923+
},
924+
&parser.CommentOption{
925+
Comment: pos(38),
926+
Expr: &parser.FloatLit{
927+
ValuePos: pos(46),
928+
Value: "23.5",
929+
},
930+
},
931+
&parser.CommentOption{
932+
Comment: pos(51),
933+
Expr: &parser.BoolLit{
934+
ValuePos: pos(59),
935+
Value: true,
936+
},
937+
},
938+
&parser.CommentOption{
939+
Comment: pos(64),
940+
Expr: &parser.StringLit{
941+
IsBlob: true,
942+
ValuePos: pos(72),
943+
Value: "foo",
944+
},
945+
},
946+
&parser.CommentOption{
947+
Comment: pos(79),
948+
Expr: &parser.NullLit{
949+
ValuePos: pos(87),
950+
},
951+
},
952+
},
953+
})
903954

955+
AssertParseStatementError(t, `CREATE`, `1:1: expected DATABASE, TABLE, VIEW or FUNCTION`)
904956
AssertParseStatementError(t, `CREATE DATABASE`, `1:15: expected database name, found 'EOF'`)
957+
AssertParseStatementError(t, `CREATE DATABASE IF`, `1:18: expected NOT, found 'EOF'`)
958+
AssertParseStatementError(t, `CREATE DATABASE IF NOT`, `1:22: expected EXISTS, found 'EOF'`)
905959
AssertParseStatementError(t, `CREATE DATABASE db (`, `1:20: expected semicolon or EOF, found '('`)
906960
AssertParseStatementError(t, `CREATE DATABASE db extra`, `1:20: expected semicolon or EOF, found extra`)
961+
AssertParseStatementError(t, `CREATE DATABASE db WITH`, `1:20: expected at least one option after WITH`)
907962
AssertParseStatementError(t, `CREATE DATABASE db WITH UNITS`, `1:29: expected literal, found 'EOF'`)
963+
AssertParseStatementError(t, `CREATE DATABASE db WITH COMMENT`, `1:31: expected literal, found 'EOF'`)
964+
908965
})
909966

910967
t.Run("CreateTable", func(t *testing.T) {
@@ -3769,6 +3826,29 @@ func TestParser_ParseStatement(t *testing.T) {
37693826
},
37703827
},
37713828
})
3829+
AssertParseStatement(t, `UPDATE tbl SET (x, y) = {1, 2}`, &parser.UpdateStatement{
3830+
Update: pos(0),
3831+
Table: &parser.QualifiedTableName{
3832+
Name: &parser.Ident{NamePos: pos(7), Name: "tbl"},
3833+
},
3834+
Set: pos(11),
3835+
Assignments: []*parser.Assignment{
3836+
{
3837+
Lparen: pos(15),
3838+
Rparen: pos(20),
3839+
Columns: []*parser.Ident{{NamePos: pos(16), Name: "x"}, {NamePos: pos(19), Name: "y"}},
3840+
Eq: pos(22),
3841+
Expr: &parser.TupleLiteralExpr{
3842+
Lbrace: pos(24),
3843+
Rbrace: pos(29),
3844+
Members: []parser.Expr{
3845+
&parser.IntegerLit{ValuePos: pos(25), Value: "1"},
3846+
&parser.IntegerLit{ValuePos: pos(28), Value: "2"},
3847+
},
3848+
},
3849+
},
3850+
},
3851+
})
37723852
AssertParseStatement(t, `UPDATE tbl SET x = 1 WHERE y = 2`, &parser.UpdateStatement{
37733853
Update: pos(0),
37743854
Table: &parser.QualifiedTableName{

sql3/parser/scanner_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ func TestScanner_Scan(t *testing.T) {
4141
t.Run("NoEndQuote", func(t *testing.T) {
4242
AssertScan(t, `'unfinished`, parser.ILLEGAL, `'unfinished`)
4343
})
44+
t.Run("NoEndQuoteNL", func(t *testing.T) {
45+
AssertScan(t, "'unfinished\n", parser.UNTERMSTRING, `'unfinished`)
46+
})
4447
})
4548
t.Run("BLOB", func(t *testing.T) {
4649
t.Run("LowerX", func(t *testing.T) {
@@ -52,6 +55,9 @@ func TestScanner_Scan(t *testing.T) {
5255
t.Run("NoEndQuote", func(t *testing.T) {
5356
AssertScan(t, `x'0123`, parser.ILLEGAL, `x'0123`)
5457
})
58+
t.Run("QuotedQuote", func(t *testing.T) {
59+
AssertScan(t, `x'01''23'`, parser.BLOB, `01'23`)
60+
})
5561
})
5662

5763
t.Run("INTEGER", func(t *testing.T) {

0 commit comments

Comments
 (0)