Skip to content

Commit 5ccde5d

Browse files
authored
Merge pull request #2 from monstermichl/main
Add support for placeholders
2 parents dfda5d4 + a49c86e commit 5ccde5d

File tree

5 files changed

+77
-3
lines changed

5 files changed

+77
-3
lines changed

README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ DELETE:
8484
DELETE FROM table1 WHERE c1 == 5 AND c3 == "quoted string"
8585
```
8686

87+
## Placeholders
88+
It is possible to use placeholders by including identifiers between curly brackets.
89+
```
90+
SELECT c1, c2 FROM table1 WHERE c3 == {0} AND c4 == {p}
91+
```
92+
8793
## Tests
8894

8995
To make sure that the code is fully tested and covered:
@@ -99,4 +105,4 @@ ok github.com/krasun/gosqlparser 0.470s
99105

100106
## License
101107

102-
**gosqlparser** is released under [the MIT license](LICENSE).
108+
**gosqlparser** is released under [the MIT license](LICENSE).

lexer.go

+20
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const (
3737
tokenDelimeter // ','
3838
tokenLeftParenthesis // '('
3939
tokenRightParenthesis // ')'
40+
tokenPlaceholder // '?'
4041
tokenInteger // integer
4142
tokenString // string including quotes
4243
tokenAnd // AND
@@ -70,6 +71,7 @@ var tokenToString = map[tokenType]string{
7071
tokenDelimeter: "delimeter",
7172
tokenLeftParenthesis: "leftParenthesis",
7273
tokenRightParenthesis: "rightParenthesis",
74+
tokenPlaceholder: "placeholder",
7375
tokenInteger: "integer",
7476
tokenString: "string",
7577
tokenAnd: "AND",
@@ -227,6 +229,9 @@ func lexStatement(l *lexer) lexFunc {
227229
case r == ')':
228230
l.produce(tokenRightParenthesis)
229231
return lexStatement
232+
case r == '{':
233+
l.revert()
234+
return lexPlaceholder
230235
case r == '"':
231236
return lexString
232237
case r == ',':
@@ -281,6 +286,21 @@ func lexString(l *lexer) lexFunc {
281286
return lexString
282287
}
283288

289+
func lexPlaceholder(l *lexer) lexFunc {
290+
r := l.next()
291+
292+
switch r {
293+
case '}':
294+
l.produce(tokenPlaceholder)
295+
296+
return lexStatement
297+
case end:
298+
return l.errorf("expected }")
299+
}
300+
301+
return lexPlaceholder
302+
}
303+
284304
func lexIdentifier(l *lexer) lexFunc {
285305
r := l.next()
286306

lexer_test.go

+33
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,39 @@ func TestLexer(t *testing.T) {
102102
{tokenEnd, ""},
103103
},
104104
},
105+
{
106+
"full SELECT query with placeholders",
107+
"SELECT c1, c2 FROM table1 WHERE c1 == {0} AND c2 == {placeholder}",
108+
[]token{
109+
{tokenSelect, "SELECT"},
110+
{tokenSpace, " "},
111+
{tokenIdentifier, "c1"},
112+
{tokenDelimeter, ","},
113+
{tokenSpace, " "},
114+
{tokenIdentifier, "c2"},
115+
{tokenSpace, " "},
116+
{tokenFrom, "FROM"},
117+
{tokenSpace, " "},
118+
{tokenIdentifier, "table1"},
119+
{tokenSpace, " "},
120+
{tokenWhere, "WHERE"},
121+
{tokenSpace, " "},
122+
{tokenIdentifier, "c1"},
123+
{tokenSpace, " "},
124+
{tokenEquals, "=="},
125+
{tokenSpace, " "},
126+
{tokenPlaceholder, "{0}"},
127+
{tokenSpace, " "},
128+
{tokenAnd, "AND"},
129+
{tokenSpace, " "},
130+
{tokenIdentifier, "c2"},
131+
{tokenSpace, " "},
132+
{tokenEquals, "=="},
133+
{tokenSpace, " "},
134+
{tokenPlaceholder, "{placeholder}"},
135+
{tokenEnd, ""},
136+
},
137+
},
105138
{
106139
"integer",
107140
"123456789",

parser.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ type Expr interface {
172172
func (ExprIdentifier) i() {}
173173
func (ExprValueInteger) i() {}
174174
func (ExprValueString) i() {}
175+
func (ExprValuePlaceholder) i() {}
175176
func (ExprOperation) i() {}
176177

177178
// ExprIdentifier holds the name of the identifier.
@@ -189,6 +190,11 @@ type ExprValueString struct {
189190
Value string
190191
}
191192

193+
// ExprValuePlaceholder holds the placeholder.
194+
type ExprValuePlaceholder struct {
195+
Value string
196+
}
197+
192198
// ExprOperation represents operation with == or AND operators.
193199
type ExprOperation struct {
194200
Left Expr
@@ -636,7 +642,7 @@ func parseExprOperation(p *parser, terminalTokenTypes ...tokenType) (Expr, *toke
636642
operator = OperatorEquals
637643
}
638644

639-
t, err = p.scanFor(tokenIdentifier, tokenInteger, tokenString)
645+
t, err = p.scanFor(tokenIdentifier, tokenInteger, tokenString, tokenPlaceholder)
640646
if err != nil {
641647
return nil, nil, err
642648
}
@@ -649,6 +655,8 @@ func parseExprOperation(p *parser, terminalTokenTypes ...tokenType) (Expr, *toke
649655
right = ExprValueInteger{t.value}
650656
case tokenString:
651657
right = ExprValueString{t.value}
658+
case tokenPlaceholder:
659+
right = ExprValuePlaceholder{t.value}
652660
}
653661

654662
var expr = ExprOperation{left, operator, right}

parser_test.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func TestParser(t *testing.T) {
6969
"unfinished WHERE statement",
7070
"SELECT col FROM table1 WHERE a ==",
7171
nil,
72-
fmt.Errorf("expected identifier, integer, string, but got end: \"\""),
72+
fmt.Errorf("expected identifier, integer, string, placeholder, but got end: \"\""),
7373
},
7474
{
7575
"unfinished WHERE statement",
@@ -173,6 +173,12 @@ func TestParser(t *testing.T) {
173173
&sql.Select{"table1", []string{"col1", "col2"}, &sql.Where{sql.ExprOperation{sql.ExprIdentifier{"col1"}, sql.OperatorEquals, sql.ExprIdentifier{"col2"}}}, ""},
174174
nil,
175175
},
176+
{
177+
"SELECT FROM with simple WHERE with placeholder",
178+
"SELECT col1 FROM table1 WHERE col1 == {0}",
179+
&sql.Select{"table1", []string{"col1"}, &sql.Where{sql.ExprOperation{sql.ExprIdentifier{"col1"}, sql.OperatorEquals, sql.ExprValuePlaceholder{"{0}"}}}, ""},
180+
nil,
181+
},
176182
{
177183
"SELECT FROM with WHERE AND LIMIT",
178184
"SELECT col1, col2 FROM table1 WHERE col1 == col2 AND col3 == col4 LIMIT 10",
@@ -220,6 +226,7 @@ func TestParser(t *testing.T) {
220226
for _, testCase := range testCases {
221227
t.Run(testCase.name, func(t *testing.T) {
222228
statement, err := sql.Parse(testCase.input)
229+
223230
if testCase.err != nil {
224231
if err == nil {
225232
t.Errorf("expected error \"%s\", but got nil", testCase.err)

0 commit comments

Comments
 (0)