Skip to content

Commit a6c2085

Browse files
committed
Support $NNN-style named parameter. Close mattn#187
1 parent 5253daf commit a6c2085

2 files changed

Lines changed: 93 additions & 4 deletions

File tree

sqlite3.go

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ type SQLiteTx struct {
121121
type SQLiteStmt struct {
122122
c *SQLiteConn
123123
s *C.sqlite3_stmt
124+
nv int
124125
t string
125126
closed bool
126127
cls bool
@@ -368,7 +369,19 @@ func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) {
368369
if tail != nil && C.strlen(tail) > 0 {
369370
t = strings.TrimSpace(C.GoString(tail))
370371
}
371-
ss := &SQLiteStmt{c: c, s: s, t: t}
372+
nv := int(C.sqlite3_bind_parameter_count(s))
373+
if nv > 0 {
374+
pn := C.GoString(C.sqlite3_bind_parameter_name(s, 1))
375+
/* TODO: map argument for named parameters
376+
if len(pn) > 0 && pn[0] == '$' && pn[1] != '1' {
377+
nv = -1
378+
}
379+
*/
380+
if len(pn) > 0 && pn[0] != '?' {
381+
nv = -1
382+
}
383+
}
384+
ss := &SQLiteStmt{c: c, s: s, nv: nv, t: t}
372385
runtime.SetFinalizer(ss, (*SQLiteStmt).Close)
373386
return ss, nil
374387
}
@@ -392,7 +405,12 @@ func (s *SQLiteStmt) Close() error {
392405

393406
// Return a number of parameters.
394407
func (s *SQLiteStmt) NumInput() int {
395-
return int(C.sqlite3_bind_parameter_count(s.s))
408+
return s.nv
409+
}
410+
411+
type bindArg struct {
412+
n int
413+
v driver.Value
396414
}
397415

398416
func (s *SQLiteStmt) bind(args []driver.Value) error {
@@ -401,8 +419,43 @@ func (s *SQLiteStmt) bind(args []driver.Value) error {
401419
return s.c.lastError()
402420
}
403421

404-
for i, v := range args {
405-
n := C.int(i + 1)
422+
var vargs []bindArg
423+
narg := len(args)
424+
if s.nv == -1 {
425+
/* TODO: map argument for named parameters
426+
if narg == 1 {
427+
if m, ok := args[0].(map[string]driver.Value); ok {
428+
for k, v := range m {
429+
pn := C.CString(k)
430+
if pi := int(C.sqlite3_bind_parameter_index(s.s, pn)); pi > 0 {
431+
println(pi)
432+
vargs = append(vargs, bindArg{pi, v})
433+
}
434+
C.free(unsafe.Pointer(pn))
435+
}
436+
}
437+
narg = 0
438+
}
439+
*/
440+
if narg > 0 {
441+
for i := 0; i < narg; i++ {
442+
pn := C.CString(fmt.Sprint(i + 1))
443+
if pi := int(C.sqlite3_bind_parameter_index(s.s, pn)); pi > 0 {
444+
vargs = append(vargs, bindArg{pi, args[i]})
445+
}
446+
C.free(unsafe.Pointer(pn))
447+
}
448+
}
449+
} else {
450+
vargs = make([]bindArg, narg)
451+
for i, v := range args {
452+
vargs[i] = bindArg{i + 1, v}
453+
}
454+
}
455+
456+
for _, varg := range vargs {
457+
n := C.int(varg.n)
458+
v := varg.v
406459
switch v := v.(type) {
407460
case nil:
408461
rv = C.sqlite3_bind_null(s.s, n)

sqlite3_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,3 +909,39 @@ func TestVersion(t *testing.T) {
909909
t.Errorf("Version failed %q, %d, %q\n", s, n, id)
910910
}
911911
}
912+
913+
func TestNumberNamedParams(t *testing.T) {
914+
tempFilename := TempFilename()
915+
db, err := sql.Open("sqlite3", tempFilename)
916+
if err != nil {
917+
t.Fatal("Failed to open database:", err)
918+
}
919+
defer os.Remove(tempFilename)
920+
defer db.Close()
921+
922+
_, err = db.Exec(`
923+
create table foo (id integer, name text, extra text);
924+
`)
925+
if err != nil {
926+
t.Error("Failed to call db.Query:", err)
927+
}
928+
929+
_, err = db.Exec(`insert into foo(id, name, extra)) values($1, $2, $2)`, 1, "foo")
930+
if err != nil {
931+
t.Error("Failed to call db.Exec:", err)
932+
}
933+
934+
row := db.QueryRow(`select id, name, extra where id = $1 and extra = $2`, 1, "foo")
935+
if row == nil {
936+
t.Error("Failed to call db.QueryRow")
937+
}
938+
var id int
939+
var extra string
940+
err = row.Scan(&id, &extra)
941+
if err != nil {
942+
t.Error("Failed to db.Scan:", err)
943+
}
944+
if id != 1 || extra != "foo" {
945+
t.Error("Failed to db.QueryRow: not matched results")
946+
}
947+
}

0 commit comments

Comments
 (0)