FIXME: this function isn't safe for unicode named params, as a failing test can testify. This is not a regression but a failure of the original code as well. It should be modified to range over runes in a string rather than bytes, even though this is less convenient and slower. Hopefully the addi
(qs []byte, bindType int)
| 329 | // compile a NamedQuery into an unbound query (using the '?' bindvar) and |
| 330 | // a list of names. |
| 331 | func compileNamedQuery(qs []byte, bindType int) (query string, names []string, err error) { |
| 332 | names = make([]string, 0, 10) |
| 333 | rebound := make([]byte, 0, len(qs)) |
| 334 | |
| 335 | inName := false |
| 336 | last := len(qs) - 1 |
| 337 | currentVar := 1 |
| 338 | name := make([]byte, 0, 10) |
| 339 | |
| 340 | for i, b := range qs { |
| 341 | // a ':' while we're in a name is an error |
| 342 | if b == ':' { |
| 343 | // if this is the second ':' in a '::' escape sequence, append a ':' |
| 344 | if inName && i > 0 && qs[i-1] == ':' { |
| 345 | rebound = append(rebound, ':') |
| 346 | inName = false |
| 347 | continue |
| 348 | } else if inName { |
| 349 | err = errors.New("unexpected `:` while reading named param at " + strconv.Itoa(i)) |
| 350 | return query, names, err |
| 351 | } |
| 352 | inName = true |
| 353 | name = []byte{} |
| 354 | } else if inName && i > 0 && b == '=' && len(name) == 0 { |
| 355 | rebound = append(rebound, ':', '=') |
| 356 | inName = false |
| 357 | continue |
| 358 | // if we're in a name, and this is an allowed character, continue |
| 359 | } else if inName && (unicode.IsOneOf(allowedBindRunes, rune(b)) || b == '_' || b == '.') && i != last { |
| 360 | // append the byte to the name if we are in a name and not on the last byte |
| 361 | name = append(name, b) |
| 362 | // if we're in a name and it's not an allowed character, the name is done |
| 363 | } else if inName { |
| 364 | inName = false |
| 365 | // if this is the final byte of the string and it is part of the name, then |
| 366 | // make sure to add it to the name |
| 367 | if i == last && unicode.IsOneOf(allowedBindRunes, rune(b)) { |
| 368 | name = append(name, b) |
| 369 | } |
| 370 | // add the string representation to the names list |
| 371 | names = append(names, string(name)) |
| 372 | // add a proper bindvar for the bindType |
| 373 | switch bindType { |
| 374 | // oracle only supports named type bind vars even for positional |
| 375 | case NAMED: |
| 376 | rebound = append(rebound, ':') |
| 377 | rebound = append(rebound, name...) |
| 378 | case QUESTION, UNKNOWN: |
| 379 | rebound = append(rebound, '?') |
| 380 | case DOLLAR: |
| 381 | rebound = append(rebound, '$') |
| 382 | for _, b := range strconv.Itoa(currentVar) { |
| 383 | rebound = append(rebound, byte(b)) |
| 384 | } |
| 385 | currentVar++ |
| 386 | case AT: |
| 387 | rebound = append(rebound, '@', 'p') |
| 388 | for _, b := range strconv.Itoa(currentVar) { |
no outgoing calls