Repository: proullon/ramsql (go)

Issue:
I can't add in false values in an insert statement
I use this with sqlx primarily the "NamedExec" func. I can't insert false values into the db. It throws a syntax error. 

I can enter in a true value. The "empty" value for a boolean is false. When a struct has an empty bool I can't add that value to the db.

#92 

Pull request description:
fix: (close #93) boolean parsing in INSERT query

Patch:
diff --git a/engine/parser/insert.go b/engine/parser/insert.go
new file mode 100644
index 0000000..e51509f
--- /dev/null
+++ b/engine/parser/insert.go
@@ -0,0 +1,156 @@
+package parser
+
+// Parses an INSERT statement.
+//
+// The generated AST is as follows:
+//
+//	|-> "INSERT" (InsertToken)
+//	    |-> "INTO" (IntoToken)
+//	        |-> table name
+//	            |-> column name
+//	            |-> (...)
+//	    |-> "VALUES" (ValuesToken)
+//	        |-> "(" (BracketOpeningToken)
+//	            |-> value
+//	            |-> (...)
+//	        |-> (...)
+//	    |-> "RETURNING" (ReturningToken) (optional)
+//	        |-> column name
+func (p *parser) parseInsert() (*Instruction, error) {
+	i := &Instruction{}
+
+	// Set INSERT decl
+	insertDecl, err := p.consumeToken(InsertToken)
+	if err != nil {
+		return nil, err
+	}
+	i.Decls = append(i.Decls, insertDecl)
+
+	// should be INTO
+	intoDecl, err := p.consumeToken(IntoToken)
+	if err != nil {
+		return nil, err
+	}
+	insertDecl.Add(intoDecl)
+
+	// should be table Name
+	tableDecl, err := p.parseTableName()
+	if err != nil {
+		return nil, err
+	}
+	intoDecl.Add(tableDecl)
+
+	_, err = p.consumeToken(BracketOpeningToken)
+	if err != nil {
+		return nil, err
+	}
+
+	// concerned attribute
+	for {
+		decl, err := p.parseListElement()
+		if err != nil {
+			return nil, err
+		}
+		tableDecl.Add(decl)
+
+		if p.is(BracketClosingToken) {
+			if _, err = p.consumeToken(BracketClosingToken); err != nil {
+				return nil, err
+			}
+
+			break
+		}
+
+		_, err = p.consumeToken(CommaToken)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// should be VALUES
+	valuesDecl, err := p.consumeToken(ValuesToken)
+	if err != nil {
+		return nil, err
+	}
+	insertDecl.Add(valuesDecl)
+
+	for {
+		openingBracketDecl, err := p.consumeToken(BracketOpeningToken)
+		if err != nil {
+			return nil, err
+		}
+		valuesDecl.Add(openingBracketDecl)
+
+		// should be a list of values for specified attributes
+		for {
+			decl, err := p.parseListElement()
+			if err != nil {
+				return nil, err
+			}
+			openingBracketDecl.Add(decl)
+
+			if p.is(BracketClosingToken) {
+				p.consumeToken(BracketClosingToken)
+				break
+			}
+
+			_, err = p.consumeToken(CommaToken)
+			if err != nil {
+				return nil, err
+			}
+		}
+
+		if p.is(CommaToken) {
+			p.consumeToken(CommaToken)
+			continue
+		}
+
+		break
+	}
+
+	// we may have `returning "something"` here
+	if retDecl, err := p.consumeToken(ReturningToken); err == nil {
+		insertDecl.Add(retDecl)
+
+		// returned attribute
+		attrDecl, err := p.parseAttribute()
+		if err != nil {
+			return nil, err
+		}
+		retDecl.Add(attrDecl)
+	}
+
+	return i, nil
+}
+
+func (p *parser) parseListElement() (*Decl, error) {
+	quoted := false
+
+	// In case of INSERT, can be DEFAULT here
+	if p.is(DefaultToken) {
+		v, err := p.consumeToken(DefaultToken)
+		if err != nil {
+			return nil, err
+		}
+		return v, nil
+	}
+
+	if p.is(SimpleQuoteToken) || p.is(DoubleQuoteToken) {
+		quoted = true
+		p.next()
+	}
+
+	var valueDecl *Decl
+	valueDecl, err := p.consumeToken(FloatToken, StringToken, NumberToken, NullToken, DateToken, NowToken, ArgToken, NamedArgToken, FalseToken)
+	if err != nil {
+		return nil, err
+	}
+
+	if quoted {
+		if _, err := p.consumeToken(SimpleQuoteToken, DoubleQuoteToken); err != nil {
+			return nil, err
+		}
+	}
+
+	return valueDecl, nil
+}
diff --git a/engine/parser/lexer.go b/engine/parser/lexer.go
index 4665923..a5529d2 100644
--- a/engine/parser/lexer.go
+++ b/engine/parser/lexer.go
@@ -196,7 +196,7 @@ func (l *lexer) lex(instruction []byte) ([]Token, error) {
 	matchers = append(matchers, l.genericStringMatcher("for", ForToken))
 	matchers = append(matchers, l.genericStringMatcher("default", DefaultToken))
 	matchers = append(matchers, l.genericStringMatcher("localtimestamp", LocalTimestampToken))
-	matchers = append(matchers, l.genericStringMatcher("false", LocalTimestampToken))
+	matchers = append(matchers, l.genericStringMatcher("false", FalseToken))
 	matchers = append(matchers, l.genericStringMatcher("unique", UniqueToken))
 	matchers = append(matchers, l.genericStringMatcher("now()", NowToken))
 	matchers = append(matchers, l.genericStringMatcher("offset", OffsetToken))
diff --git a/engine/parser/parser.go b/engine/parser/parser.go
index 7d8980d..fad8d05 100644
--- a/engine/parser/parser.go
+++ b/engine/parser/parser.go
@@ -219,129 +219,6 @@ func (p *parser) parseUpdate() (*Instruction, error) {
 	return i, nil
 }
 
-// Parses an INSERT statement.
-//
-// The generated AST is as follows:
-//
-//	|-> "INSERT" (InsertToken)
-//	    |-> "INTO" (IntoToken)
-//	        |-> table name
-//	            |-> column name
-//	            |-> (...)
-//	    |-> "VALUES" (ValuesToken)
-//	        |-> "(" (BracketOpeningToken)
-//	            |-> value
-//	            |-> (...)
-//	        |-> (...)
-//	    |-> "RETURNING" (ReturningToken) (optional)
-//	        |-> column name
-func (p *parser) parseInsert() (*Instruction, error) {
-	i := &Instruction{}
-
-	// Set INSERT decl
-	insertDecl, err := p.consumeToken(InsertToken)
-	if err != nil {
-		return nil, err
-	}
-	i.Decls = append(i.Decls, insertDecl)
-
-	// should be INTO
-	intoDecl, err := p.consumeToken(IntoToken)
-	if err != nil {
-		return nil, err
-	}
-	insertDecl.Add(intoDecl)
-
-	// should be table Name
-	tableDecl, err := p.parseTableName()
-	if err != nil {
-		return nil, err
-	}
-	intoDecl.Add(tableDecl)
-
-	_, err = p.consumeToken(BracketOpeningToken)
-	if err != nil {
-		return nil, err
-	}
-
-	// concerned attribute
-	for {
-		decl, err := p.parseListElement()
-		if err != nil {
-			return nil, err
-		}
-		tableDecl.Add(decl)
-
-		if p.is(BracketClosingToken) {
-			if _, err = p.consumeToken(BracketClosingToken); err != nil {
-				return nil, err
-			}
-
-			break
-		}
-
-		_, err = p.consumeToken(CommaToken)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	// should be VALUES
-	valuesDecl, err := p.consumeToken(ValuesToken)
-	if err != nil {
-		return nil, err
-	}
-	insertDecl.Add(valuesDecl)
-
-	for {
-		openingBracketDecl, err := p.consumeToken(BracketOpeningToken)
-		if err != nil {
-			return nil, err
-		}
-		valuesDecl.Add(openingBracketDecl)
-
-		// should be a list of values for specified attributes
-		for {
-			decl, err := p.parseListElement()
-			if err != nil {
-				return nil, err
-			}
-			openingBracketDecl.Add(decl)
-
-			if p.is(BracketClosingToken) {
-				p.consumeToken(BracketClosingToken)
-				break
-			}
-
-			_, err = p.consumeToken(CommaToken)
-			if err != nil {
-				return nil, err
-			}
-		}
-
-		if p.is(CommaToken) {
-			p.consumeToken(CommaToken)
-			continue
-		}
-
-		break
-	}
-
-	// we may have `returning "something"` here
-	if retDecl, err := p.consumeToken(ReturningToken); err == nil {
-		insertDecl.Add(retDecl)
-
-		// returned attribute
-		attrDecl, err := p.parseAttribute()
-		if err != nil {
-			return nil, err
-		}
-		retDecl.Add(attrDecl)
-	}
-
-	return i, nil
-}
-
 func (p *parser) parseType() (*Decl, error) {
 	typeDecl, err := p.consumeToken(FloatToken, DateToken, DecimalToken, NumberToken, StringToken)
 	if err != nil {
@@ -812,38 +689,6 @@ func (p *parser) parseJoin() (*Decl, error) {
 	return joinDecl, nil
 }
 
-func (p *parser) parseListElement() (*Decl, error) {
-	quoted := false
-
-	// In case of INSERT, can be DEFAULT here
-	if p.is(DefaultToken) {
-		v, err := p.consumeToken(DefaultToken)
-		if err != nil {
-			return nil, err
-		}
-		return v, nil
-	}
-
-	if p.is(SimpleQuoteToken) || p.is(DoubleQuoteToken) {
-		quoted = true
-		p.next()
-	}
-
-	var valueDecl *Decl
-	valueDecl, err := p.consumeToken(FloatToken, StringToken, NumberToken, NullToken, DateToken, NowToken, ArgToken, NamedArgToken)
-	if err != nil {
-		return nil, err
-	}
-
-	if quoted {
-		if _, err := p.consumeToken(SimpleQuoteToken, DoubleQuoteToken); err != nil {
-			return nil, err
-		}
-	}
-
-	return valueDecl, nil
-}
-
 func (p *parser) next() error {
 	if !p.hasNext() {
 		return fmt.Errorf("Unexpected end")


Test patch:
diff --git a/driver/driver_test.go b/driver/driver_test.go
index d1e1484..a33077c 100644
--- a/driver/driver_test.go
+++ b/driver/driver_test.go
@@ -1473,12 +1473,12 @@ func TestInsertSingle(t *testing.T) {
 	}
 	defer db.Close()
 
-	_, err = db.Exec("CREATE TABLE cat (id INT AUTOINCREMENT, breed TEXT, name TEXT)")
+	_, err = db.Exec("CREATE TABLE cat (id INT AUTOINCREMENT, breed TEXT, name TEXT, funny BOOLEAN)")
 	if err != nil {
 		t.Fatalf("sql.Exec: Error: %s\n", err)
 	}
 
-	result, err := db.Exec("INSERT INTO cat (breed, name) VALUES ('indeterminate', 'Uhura')")
+	result, err := db.Exec("INSERT INTO cat (breed, name, funny) VALUES ('indeterminate', 'Uhura', false)")
 	if err != nil {
 		t.Fatalf("Cannot insert into table account: %s", err)
 	}
@@ -1496,14 +1496,17 @@ func TestInsertSingle(t *testing.T) {
 		t.Fatalf("Cannot check last inserted ID: %s", err)
 	}
 
-	row := db.QueryRow("SELECT breed, name FROM cat WHERE id = ?", insertedId)
+	row := db.QueryRow("SELECT breed, name, funny FROM cat WHERE id = ?", insertedId)
 	if row == nil {
 		t.Fatalf("sql.Query failed")
 	}
 
 	var breed string
 	var name string
-	err = row.Scan(&breed, &name)
+	var funny bool
+	funny = true
+
+	err = row.Scan(&breed, &name, &funny)
 	if err != nil {
 		t.Fatalf("row.Scan: %s", err)
 	}
@@ -1511,6 +1514,25 @@ func TestInsertSingle(t *testing.T) {
 	if breed != "indeterminate" || name != "Uhura" {
 		t.Fatalf("Expected breed 'indeterminate' and name 'Uhura', got breed '%v' and name '%v'", breed, name)
 	}
+
+	if funny != false {
+		t.Fatalf("Expected funny=false, got true")
+	}
+
+	result, err = db.Exec("INSERT INTO cat (breed, name, funny) VALUES ('indeterminate', 'Kuhura', true)")
+	if err != nil {
+		t.Fatalf("Cannot insert into table account: %s", err)
+	}
+
+	err = db.QueryRow("SELECT breed, name, funny FROM cat WHERE name = ?", "Kuhura").Scan(&breed, &name, &funny)
+	if err != nil {
+		t.Fatalf("row.Scan: %s", err)
+	}
+
+	if funny != true {
+		t.Fatalf("Expected funny=true, got false")
+	}
+
 }
 
 func TestInsertSingleReturning(t *testing.T) {