Skip to content

Commit

Permalink
fix: handling named parameter in string literals (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
hirasawayuki authored Mar 10, 2024
1 parent 332c56a commit fc7c6dc
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 2 deletions.
52 changes: 50 additions & 2 deletions named.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,14 +332,58 @@ func compileNamedQuery(qs []byte, bindType int) (query string, names []string, e
names = make([]string, 0, 10)
rebound := make([]byte, 0, len(qs))

// the type of quote (' or ") that started the string literal
var quoteLeft byte
inString := false
var escaped bool
inName := false
last := len(qs) - 1
currentVar := 1
name := make([]byte, 0, 10)

for i, b := range qs {
// a ':' while we're in a name is an error
if b == ':' {
if b == '\'' || b == '"' {
// start of a string literal
if !inString {
inString = true
quoteLeft = b
rebound = append(rebound, b)

continue
}

// ignore the quote if it is escaped
if i > 0 && qs[i-1] == '\\' {
rebound = append(rebound, b)
continue
}

// end of the string literal if matching quote is found
if quoteLeft == b {
inString = false
rebound = append(rebound, b)
continue
}

// handle other quotes inside the string literal (ex: "'name'" or '"name"')
rebound = append(rebound, b)
continue

// a ':' while we're in a name is an error
} else if b == ':' {
if inString {
// mark as escaped if '::' sequence is found
if i > 0 && qs[i-1] == ':' && !escaped {
escaped = true
continue
}

// if not escaped, reset the flag and append colon as it's part of the string
rebound = append(rebound, b)
escaped = false
continue
}

// if this is the second ':' in a '::' escape sequence, append a ':'
if inName && i > 0 && qs[i-1] == ':' {
rebound = append(rebound, ':')
Expand Down Expand Up @@ -402,6 +446,10 @@ func compileNamedQuery(qs []byte, bindType int) (query string, names []string, e
}
}

if inString {
return query, names, errors.New("string literal not closed, missing terminating quote")
}

return string(rebound), names, err
}

Expand Down
24 changes: 24 additions & 0 deletions named_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,30 @@ func TestCompileQuery(t *testing.T) {
T: `SELECT @name := "name", @p1, @p2, @p3`,
V: []string{"age", "first", "last"},
},
{
Q: `SELECT * FROM users WHERE id = :id AND name = ':name'`,
R: `SELECT * FROM users WHERE id = ? AND name = ':name'`,
D: `SELECT * FROM users WHERE id = $1 AND name = ':name'`,
T: `SELECT * FROM users WHERE id = @p1 AND name = ':name'`,
N: `SELECT * FROM users WHERE id = :id AND name = ':name'`,
V: []string{"id"},
},
{
Q: `SELECT * FROM users WHERE id = :id AND name = '":name"'`,
R: `SELECT * FROM users WHERE id = ? AND name = '":name"'`,
D: `SELECT * FROM users WHERE id = $1 AND name = '":name"'`,
T: `SELECT * FROM users WHERE id = @p1 AND name = '":name"'`,
N: `SELECT * FROM users WHERE id = :id AND name = '":name"'`,
V: []string{"id"},
},
{
Q: `SELECT * FROM users WHERE id = :id AND name = '\':name\''`,
R: `SELECT * FROM users WHERE id = ? AND name = '\':name\''`,
D: `SELECT * FROM users WHERE id = $1 AND name = '\':name\''`,
T: `SELECT * FROM users WHERE id = @p1 AND name = '\':name\''`,
N: `SELECT * FROM users WHERE id = :id AND name = '\':name\''`,
V: []string{"id"},
},
/* This unicode awareness test sadly fails, because of our byte-wise worldview.
* We could certainly iterate by Rune instead, though it's a great deal slower,
* it's probably the RightWay(tm)
Expand Down

0 comments on commit fc7c6dc

Please sign in to comment.