Repository: proullon/ramsql
Pull request #68 (2023-02-21 15:13:20)

Fix/67


Patch:
diff --git a/engine/parser/parser.go b/engine/parser/parser.go
index e9b1367..5d2b7ec 100644
--- a/engine/parser/parser.go
+++ b/engine/parser/parser.go
@@ -392,48 +392,6 @@ func (p *parser) parseOrderBy(selectDecl *Decl) error {
 	return nil
 }
 
-func (p *parser) parseWhere(selectDecl *Decl) error {
-
-	// May be WHERE  here
-	// Can be ORDER BY if WHERE cause if implicit
-	whereDecl, err := p.consumeToken(WhereToken)
-	if err != nil {
-		return err
-	}
-	selectDecl.Add(whereDecl)
-
-	// Now should be a list of: Attribute and Operator and Value
-	gotClause := false
-	for {
-		if !p.hasNext() && gotClause {
-			break
-		}
-
-		if p.is(OrderToken, LimitToken, ForToken) {
-			break
-		}
-
-		attributeDecl, err := p.parseCondition()
-		if err != nil {
-			return err
-		}
-		whereDecl.Add(attributeDecl)
-
-		if p.is(AndToken, OrToken) {
-			linkDecl, err := p.consumeToken(p.cur().Token)
-			if err != nil {
-				return err
-			}
-			whereDecl.Add(linkDecl)
-		}
-
-		// Got at least one clause
-		gotClause = true
-	}
-
-	return nil
-}
-
 // parseBuiltinFunc looks for COUNT,MAX,MIN
 func (p *parser) parseBuiltinFunc() (*Decl, error) {
 	var d *Decl
@@ -610,97 +568,6 @@ func (p *parser) parseAttribution() (*Decl, error) {
 	return attributeDecl, nil
 }
 
-func (p *parser) parseCondition() (*Decl, error) {
-
-	// We may have the WHERE 1 condition
-	if t := p.cur(); t.Token == NumberToken && t.Lexeme == "1" {
-		attributeDecl := NewDecl(t)
-		p.next()
-		// in case of 1 = 1
-		if p.cur().Token == EqualityToken {
-			t, err := p.isNext(NumberToken)
-			if err == nil && t.Lexeme == "1" {
-				p.consumeToken(EqualityToken)
-				p.consumeToken(NumberToken)
-			}
-		}
-		return attributeDecl, nil
-	}
-
-	// Attribute
-	attributeDecl, err := p.parseAttribute()
-	if err != nil {
-		return nil, err
-	}
-
-	switch p.cur().Token {
-	case EqualityToken, DistinctnessToken, LeftDipleToken, RightDipleToken, LessOrEqualToken, GreaterOrEqualToken:
-		decl, err := p.consumeToken(p.cur().Token)
-		if err != nil {
-			return nil, err
-		}
-		attributeDecl.Add(decl)
-		break
-	case InToken:
-		inDecl, err := p.parseIn()
-		if err != nil {
-			return nil, err
-		}
-		attributeDecl.Add(inDecl)
-		return attributeDecl, nil
-	case NotToken:
-		notDecl, err := p.consumeToken(p.cur().Token)
-		if err != nil {
-			return nil, err
-		}
-
-		if p.cur().Token != InToken {
-			return nil, fmt.Errorf("expected IN after NOT")
-		}
-
-		inDecl, err := p.parseIn()
-		if err != nil {
-			return nil, err
-		}
-		notDecl.Add(inDecl)
-
-		attributeDecl.Add(notDecl)
-		return attributeDecl, nil
-	case IsToken:
-		log.Debug("parseCondition: IsToken\n")
-		decl, err := p.consumeToken(IsToken)
-		if err != nil {
-			return nil, err
-		}
-		attributeDecl.Add(decl)
-		if p.cur().Token == NotToken {
-			log.Debug("parseCondition: NotToken\n")
-			notDecl, err := p.consumeToken(NotToken)
-			if err != nil {
-				return nil, err
-			}
-			decl.Add(notDecl)
-		}
-		if p.cur().Token == NullToken {
-			log.Debug("parseCondition: NullToken\n")
-			nullDecl, err := p.consumeToken(NullToken)
-			if err != nil {
-				return nil, err
-			}
-			decl.Add(nullDecl)
-		}
-		return attributeDecl, nil
-	}
-
-	// Value
-	valueDecl, err := p.parseValue()
-	if err != nil {
-		return nil, err
-	}
-	attributeDecl.Add(valueDecl)
-	return attributeDecl, nil
-}
-
 func (p *parser) parseIn() (*Decl, error) {
 	inDecl, err := p.consumeToken(InToken)
 	if err != nil {
diff --git a/engine/parser/where.go b/engine/parser/where.go
new file mode 100644
index 0000000..232a028
--- /dev/null
+++ b/engine/parser/where.go
@@ -0,0 +1,155 @@
+package parser
+
+import (
+	"fmt"
+
+	"github.com/proullon/ramsql/engine/log"
+)
+
+func (p *parser) parseWhere(selectDecl *Decl) error {
+
+	// May be WHERE  here
+	// Can be ORDER BY if WHERE cause if implicit
+	whereDecl, err := p.consumeToken(WhereToken)
+	if err != nil {
+		return err
+	}
+	selectDecl.Add(whereDecl)
+
+	// Now should be a list of: Attribute and Operator and Value
+	gotClause := false
+	for {
+		if !p.hasNext() && gotClause {
+			break
+		}
+
+		if p.is(OrderToken, LimitToken, ForToken) {
+			break
+		}
+
+		attributeDecl, err := p.parseCondition()
+		if err != nil {
+			return err
+		}
+		whereDecl.Add(attributeDecl)
+
+		if p.is(AndToken, OrToken) {
+			linkDecl, err := p.consumeToken(p.cur().Token)
+			if err != nil {
+				return err
+			}
+			whereDecl.Add(linkDecl)
+		}
+
+		// Got at least one clause
+		gotClause = true
+	}
+
+	return nil
+}
+
+func (p *parser) parseCondition() (*Decl, error) {
+	// Optionnaly, brackets
+
+	// We may have the WHERE 1 condition
+	if t := p.cur(); t.Token == NumberToken && t.Lexeme == "1" {
+		attributeDecl := NewDecl(t)
+		p.next()
+		// in case of 1 = 1
+		if p.cur().Token == EqualityToken {
+			t, err := p.isNext(NumberToken)
+			if err == nil && t.Lexeme == "1" {
+				p.consumeToken(EqualityToken)
+				p.consumeToken(NumberToken)
+			}
+		}
+		return attributeDecl, nil
+	}
+
+	// do we have brackets ?
+	hasBracket := false
+	if p.is(BracketOpeningToken) {
+		p.consumeToken(BracketOpeningToken)
+		hasBracket = true
+	}
+
+	// Attribute
+	attributeDecl, err := p.parseAttribute()
+	if err != nil {
+		return nil, err
+	}
+
+	switch p.cur().Token {
+	case EqualityToken, DistinctnessToken, LeftDipleToken, RightDipleToken, LessOrEqualToken, GreaterOrEqualToken:
+		decl, err := p.consumeToken(p.cur().Token)
+		if err != nil {
+			return nil, err
+		}
+		attributeDecl.Add(decl)
+		break
+	case InToken:
+		inDecl, err := p.parseIn()
+		if err != nil {
+			return nil, err
+		}
+		attributeDecl.Add(inDecl)
+		return attributeDecl, nil
+	case NotToken:
+		notDecl, err := p.consumeToken(p.cur().Token)
+		if err != nil {
+			return nil, err
+		}
+
+		if p.cur().Token != InToken {
+			return nil, fmt.Errorf("expected IN after NOT")
+		}
+
+		inDecl, err := p.parseIn()
+		if err != nil {
+			return nil, err
+		}
+		notDecl.Add(inDecl)
+
+		attributeDecl.Add(notDecl)
+		return attributeDecl, nil
+	case IsToken:
+		log.Debug("parseCondition: IsToken\n")
+		decl, err := p.consumeToken(IsToken)
+		if err != nil {
+			return nil, err
+		}
+		attributeDecl.Add(decl)
+		if p.cur().Token == NotToken {
+			log.Debug("parseCondition: NotToken\n")
+			notDecl, err := p.consumeToken(NotToken)
+			if err != nil {
+				return nil, err
+			}
+			decl.Add(notDecl)
+		}
+		if p.cur().Token == NullToken {
+			log.Debug("parseCondition: NullToken\n")
+			nullDecl, err := p.consumeToken(NullToken)
+			if err != nil {
+				return nil, err
+			}
+			decl.Add(nullDecl)
+		}
+		return attributeDecl, nil
+	}
+
+	// Value
+	valueDecl, err := p.parseValue()
+	if err != nil {
+		return nil, err
+	}
+	attributeDecl.Add(valueDecl)
+
+	if hasBracket {
+		if _, err = p.consumeToken(BracketClosingToken); err != nil {
+			return nil, err
+		}
+	}
+
+	return attributeDecl, nil
+}


Test patch:
diff --git a/driver/driver_test.go b/driver/driver_test.go
index 5fad4f4..0f8d5c1 100644
--- a/driver/driver_test.go
+++ b/driver/driver_test.go
@@ -915,7 +915,6 @@ func TestJSON(t *testing.T) {
 	if err != nil {
 		t.Fatalf("sql.QueryRow: %s", err)
 	}
-	t.Logf("Result: %s\n", data)
 
 	s := struct {
 		ID   string `json:"id"`
@@ -941,7 +940,6 @@ func TestJSON(t *testing.T) {
 	if err != nil {
 		t.Fatalf("sql.QueryRow: %s", err)
 	}
-	t.Logf("Result: %s\n", data)
 
 	err = json.Unmarshal([]byte(data), &s)
 	if err != nil {
@@ -1010,3 +1008,38 @@ func TestDistinct(t *testing.T) {
 		testDistinct(t, `SELECT DISTINCT ON (surname, age) name FROM user`, 6, &name)
 	})
 }
+
+func TestBracketWhereClause(t *testing.T) {
+	log.UseTestLogger(t)
+
+	batch := []string{
+		`CREATE TABLE user (name TEXT, surname TEXT, age INT);`,
+		`INSERT INTO user (name, surname, age) VALUES ('Foo', 'Bar', 20);`,
+		`INSERT INTO user (name, surname, age) VALUES ('John', 'Doe', 32);`,
+		`INSERT INTO user (name, surname, age) VALUES ('Jane', 'Doe', 33);`,
+		`INSERT INTO user (name, surname, age) VALUES ('Joe', 'Doe', 10);`,
+		`INSERT INTO user (name, surname, age) VALUES ('Homer', 'Simpson', 40);`,
+		`INSERT INTO user (name, surname, age) VALUES ('Marge', 'Simpson', 40);`,
+		`INSERT INTO user (name, surname, age) VALUES ('Bruce', 'Wayne', 3333);`,
+	}
+
+	db, err := sql.Open("ramsql", "TestBracketWhereClause")
+	if err != nil {
+		t.Fatalf("sql.Open : Error : %s\n", err)
+	}
+
+	for _, b := range batch {
+		_, err = db.Exec(b)
+		if err != nil {
+			t.Fatalf("sql.Exec: Error: %s\n", err)
+		}
+	}
+
+	query := `SELECT * FROM "user" WHERE (age < 40)`
+
+	rows, err := db.Query(query)
+	if err != nil {
+		t.Fatalf("sql.Query: %s", err)
+	}
+	defer rows.Close()
+}