From d0078f994df6765e984ce88a88d4f4708eb188f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E8=8B=B1=E5=BC=BA?= Date: Thu, 17 Oct 2024 10:41:25 +0800 Subject: [PATCH 1/5] fix --- .gitignore | 1 + parser.go | 7 +++++++ parser_test.go | 8 ++++++++ 3 files changed, 16 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1377554 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/parser.go b/parser.go index ea93260..cad7401 100644 --- a/parser.go +++ b/parser.go @@ -2,6 +2,7 @@ package sqlparser import ( "errors" + "fmt" "io" "strings" ) @@ -220,6 +221,7 @@ func (p *Parser) parseUpsertClause() (_ *UpsertClause, err error) { return &clause, p.errorExpected(p.pos, p.tok, "CONFLICT or DUPLICATE") } p.lex() + fmt.Printf("-------- on duplicate key: %v\n", clause.DuplicateKey) // Parse optional indexed column list & WHERE conditional. if p.peek() == LP { @@ -935,6 +937,11 @@ func (p *Parser) ParseExpr() (expr Expr, err error) { func (p *Parser) parseOperand() (expr Expr, err error) { _, tok, lit := p.lex() + ////////////////// + if tok == VALUES { + _, tok, lit = p.lex() + } + ////////////////// switch tok { case IDENT, QIDENT: ident := identByNameAndTok(lit, tok) diff --git a/parser_test.go b/parser_test.go index 14c7290..2e27c54 100644 --- a/parser_test.go +++ b/parser_test.go @@ -1015,3 +1015,11 @@ func AssertParseExprError(tb testing.TB, s string, want string) { tb.Fatalf("ParseExpr()=%q, want %q", err, want) } } + +func TestSql_1(t *testing.T) { + sql := "INSERT INTO `daily_asset` (`trade_date`,`fund_account`,`client_id`,`day_income`,`hold_income`,`acc_income`,`day_income_ratio`,`acc_income_sw_ratio`,`acc_income_nav_ratio`,`market_value`,`fund_asset`,`total_asset`,`sw_total_net`,`bank_transfer_in`,`bank_transfer_out`,`acc_bank_transfer_in`,`acc_bank_transfer_out`,`net_in_balance`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `fund_asset`=VALUES(`fund_asset`),`hold_income`=VALUES(`hold_income`),`market_value`=VALUES(`market_value`),`total_asset`=VALUES(`total_asset`),`bank_transfer_in`=VALUES(`bank_transfer_in`),`bank_transfer_out`=VALUES(`bank_transfer_out`),`net_in_balance`=VALUES(`net_in_balance`)" + _, err := sqlparser.NewParser(strings.NewReader(sql)).ParseStatement() + if err != nil { + t.Fatal(err) + } +} From fbb44b299f2f49f854ae8a0c5d2939941da2041f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E8=8B=B1=E5=BC=BA?= Date: Thu, 17 Oct 2024 11:34:34 +0800 Subject: [PATCH 2/5] support values fn call --- ast.go | 15 +++++++++++++++ parser.go | 22 ++++++++++++++-------- parser_test.go | 6 +++++- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/ast.go b/ast.go index 1f892f4..5361d1a 100644 --- a/ast.go +++ b/ast.go @@ -44,6 +44,7 @@ func (*StringLit) node() {} func (*TableName) node() {} func (*Type) node() {} func (*UnaryExpr) node() {} +func (*FnCallExpr) node() {} func (*UpdateStatement) node() {} func (*UpsertClause) node() {} func (*UsingConstraint) node() {} @@ -93,6 +94,7 @@ func (*QualifiedRef) expr() {} func (*Range) expr() {} func (*StringLit) expr() {} func (*UnaryExpr) expr() {} +func (*FnCallExpr) expr() {} // ExprString returns the string representation of expr. // Returns a blank string if expr is nil. @@ -362,6 +364,19 @@ func (expr *UnaryExpr) String() string { } } +type FnCallExpr struct { + Fn Token // function + X Expr // param expression +} + +func (expr *FnCallExpr) String() string { + paran, ok := expr.X.(*ParenExpr) + if ok { + return expr.Fn.String() + "(" + paran.X.String() + ")" + } + return expr.Fn.String() + "(" + expr.X.String() + ")" +} + type BinaryExpr struct { X Expr // lhs Op Token // operator diff --git a/parser.go b/parser.go index cad7401..84be4b3 100644 --- a/parser.go +++ b/parser.go @@ -2,7 +2,6 @@ package sqlparser import ( "errors" - "fmt" "io" "strings" ) @@ -221,8 +220,6 @@ func (p *Parser) parseUpsertClause() (_ *UpsertClause, err error) { return &clause, p.errorExpected(p.pos, p.tok, "CONFLICT or DUPLICATE") } p.lex() - fmt.Printf("-------- on duplicate key: %v\n", clause.DuplicateKey) - // Parse optional indexed column list & WHERE conditional. if p.peek() == LP { p.lex() @@ -937,12 +934,21 @@ func (p *Parser) ParseExpr() (expr Expr, err error) { func (p *Parser) parseOperand() (expr Expr, err error) { _, tok, lit := p.lex() - ////////////////// - if tok == VALUES { - _, tok, lit = p.lex() - } - ////////////////// switch tok { + case VALUES: + // 左括号 + _, tok, lit = p.lex() + var expr Expr + if tok == LP { + p.unlex() + var err error + expr, err = p.parseParenExpr() + if err != nil { + return nil, err + } + } + return &FnCallExpr{Fn: VALUES, X: expr}, nil + case IDENT, QIDENT: ident := identByNameAndTok(lit, tok) if p.peek() == DOT { diff --git a/parser_test.go b/parser_test.go index 2e27c54..4a55cc1 100644 --- a/parser_test.go +++ b/parser_test.go @@ -1,6 +1,7 @@ package sqlparser_test import ( + "fmt" "strings" "testing" @@ -1018,8 +1019,11 @@ func AssertParseExprError(tb testing.TB, s string, want string) { func TestSql_1(t *testing.T) { sql := "INSERT INTO `daily_asset` (`trade_date`,`fund_account`,`client_id`,`day_income`,`hold_income`,`acc_income`,`day_income_ratio`,`acc_income_sw_ratio`,`acc_income_nav_ratio`,`market_value`,`fund_asset`,`total_asset`,`sw_total_net`,`bank_transfer_in`,`bank_transfer_out`,`acc_bank_transfer_in`,`acc_bank_transfer_out`,`net_in_balance`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `fund_asset`=VALUES(`fund_asset`),`hold_income`=VALUES(`hold_income`),`market_value`=VALUES(`market_value`),`total_asset`=VALUES(`total_asset`),`bank_transfer_in`=VALUES(`bank_transfer_in`),`bank_transfer_out`=VALUES(`bank_transfer_out`),`net_in_balance`=VALUES(`net_in_balance`)" - _, err := sqlparser.NewParser(strings.NewReader(sql)).ParseStatement() + stmt, err := sqlparser.NewParser(strings.NewReader(sql)).ParseStatement() if err != nil { t.Fatal(err) } + insert := stmt.(*sqlparser.InsertStatement) + result := insert.String() + fmt.Println(result) } From bdae1112ec51968a7e803bc9e57b23f10ce7d7ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E8=8B=B1=E5=BC=BA?= Date: Thu, 17 Oct 2024 11:48:36 +0800 Subject: [PATCH 3/5] use Call, remove FnCallExpr --- ast.go | 15 --------------- parser.go | 3 ++- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/ast.go b/ast.go index 5361d1a..1f892f4 100644 --- a/ast.go +++ b/ast.go @@ -44,7 +44,6 @@ func (*StringLit) node() {} func (*TableName) node() {} func (*Type) node() {} func (*UnaryExpr) node() {} -func (*FnCallExpr) node() {} func (*UpdateStatement) node() {} func (*UpsertClause) node() {} func (*UsingConstraint) node() {} @@ -94,7 +93,6 @@ func (*QualifiedRef) expr() {} func (*Range) expr() {} func (*StringLit) expr() {} func (*UnaryExpr) expr() {} -func (*FnCallExpr) expr() {} // ExprString returns the string representation of expr. // Returns a blank string if expr is nil. @@ -364,19 +362,6 @@ func (expr *UnaryExpr) String() string { } } -type FnCallExpr struct { - Fn Token // function - X Expr // param expression -} - -func (expr *FnCallExpr) String() string { - paran, ok := expr.X.(*ParenExpr) - if ok { - return expr.Fn.String() + "(" + paran.X.String() + ")" - } - return expr.Fn.String() + "(" + expr.X.String() + ")" -} - type BinaryExpr struct { X Expr // lhs Op Token // operator diff --git a/parser.go b/parser.go index 84be4b3..05d4ca2 100644 --- a/parser.go +++ b/parser.go @@ -946,8 +946,9 @@ func (p *Parser) parseOperand() (expr Expr, err error) { if err != nil { return nil, err } + expr = expr.(*ParenExpr).X } - return &FnCallExpr{Fn: VALUES, X: expr}, nil + return &Call{Name: &Ident{Name: "VALUES"}, Args: []Expr{expr}}, nil case IDENT, QIDENT: ident := identByNameAndTok(lit, tok) From eb949f7afca58a78013d143585d9d295dfd138d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E8=8B=B1=E5=BC=BA?= Date: Thu, 17 Oct 2024 11:52:19 +0800 Subject: [PATCH 4/5] update test fn name --- parser_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser_test.go b/parser_test.go index 4a55cc1..1373777 100644 --- a/parser_test.go +++ b/parser_test.go @@ -1017,7 +1017,7 @@ func AssertParseExprError(tb testing.TB, s string, want string) { } } -func TestSql_1(t *testing.T) { +func TestSql_support_values_call(t *testing.T) { sql := "INSERT INTO `daily_asset` (`trade_date`,`fund_account`,`client_id`,`day_income`,`hold_income`,`acc_income`,`day_income_ratio`,`acc_income_sw_ratio`,`acc_income_nav_ratio`,`market_value`,`fund_asset`,`total_asset`,`sw_total_net`,`bank_transfer_in`,`bank_transfer_out`,`acc_bank_transfer_in`,`acc_bank_transfer_out`,`net_in_balance`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `fund_asset`=VALUES(`fund_asset`),`hold_income`=VALUES(`hold_income`),`market_value`=VALUES(`market_value`),`total_asset`=VALUES(`total_asset`),`bank_transfer_in`=VALUES(`bank_transfer_in`),`bank_transfer_out`=VALUES(`bank_transfer_out`),`net_in_balance`=VALUES(`net_in_balance`)" stmt, err := sqlparser.NewParser(strings.NewReader(sql)).ParseStatement() if err != nil { From 176f6836382b239955a948c416ddfd87d4258cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E8=8B=B1=E5=BC=BA?= Date: Thu, 17 Oct 2024 13:41:21 +0800 Subject: [PATCH 5/5] add tests --- parser.go | 4 +++- parser_test.go | 20 ++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/parser.go b/parser.go index 05d4ca2..82b5c35 100644 --- a/parser.go +++ b/parser.go @@ -220,6 +220,7 @@ func (p *Parser) parseUpsertClause() (_ *UpsertClause, err error) { return &clause, p.errorExpected(p.pos, p.tok, "CONFLICT or DUPLICATE") } p.lex() + // Parse optional indexed column list & WHERE conditional. if p.peek() == LP { p.lex() @@ -936,7 +937,6 @@ func (p *Parser) parseOperand() (expr Expr, err error) { _, tok, lit := p.lex() switch tok { case VALUES: - // 左括号 _, tok, lit = p.lex() var expr Expr if tok == LP { @@ -947,6 +947,8 @@ func (p *Parser) parseOperand() (expr Expr, err error) { return nil, err } expr = expr.(*ParenExpr).X + } else { + return nil, p.errorExpected(p.pos, p.tok, "left paren") } return &Call{Name: &Ident{Name: "VALUES"}, Args: []Expr{expr}}, nil diff --git a/parser_test.go b/parser_test.go index 1373777..42e27fc 100644 --- a/parser_test.go +++ b/parser_test.go @@ -1,7 +1,6 @@ package sqlparser_test import ( - "fmt" "strings" "testing" @@ -1023,7 +1022,24 @@ func TestSql_support_values_call(t *testing.T) { if err != nil { t.Fatal(err) } + insert := stmt.(*sqlparser.InsertStatement) result := insert.String() - fmt.Println(result) + expected := "INSERT INTO `daily_asset` (`trade_date`, `fund_account`, `client_id`, `day_income`, `hold_income`, `acc_income`, `day_income_ratio`, `acc_income_sw_ratio`, `acc_income_nav_ratio`, `market_value`, `fund_asset`, `total_asset`, `sw_total_net`, `bank_transfer_in`, `bank_transfer_out`, `acc_bank_transfer_in`, `acc_bank_transfer_out`, `net_in_balance`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE `fund_asset` = VALUES(`fund_asset`), `hold_income` = VALUES(`hold_income`), `market_value` = VALUES(`market_value`), `total_asset` = VALUES(`total_asset`), `bank_transfer_in` = VALUES(`bank_transfer_in`), `bank_transfer_out` = VALUES(`bank_transfer_out`), `net_in_balance` = VALUES(`net_in_balance`)" + if result != expected { + t.Fatal("sql not equal") + } +} + +func TestSql_support_values_error(t *testing.T) { + sql := "INSERT INTO `daily_asset` (`trade_date`,`fund_account`,`client_id`,`day_income`,`hold_income`,`acc_income`,`day_income_ratio`,`acc_income_sw_ratio`,`acc_income_nav_ratio`,`market_value`,`fund_asset`,`total_asset`,`sw_total_net`,`bank_transfer_in`,`bank_transfer_out`,`acc_bank_transfer_in`,`acc_bank_transfer_out`,`net_in_balance`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `fund_asset`=VALUES `fund_asset`" + _, err := sqlparser.NewParser(strings.NewReader(sql)).ParseStatement() + if err == nil { + t.Fatal(err) + } + msg := err.Error() + expected := "1:422: expected left paren, found `fund_asset`" + if msg != expected { + t.Fatal("err msg not equal") + } }