Skip to content

Commit 98849c8

Browse files
authored
Merge pull request #3 from syohex/place-holder
Support placeholder in sqlite3-execute
2 parents 1e64d49 + 1d72244 commit 98849c8

File tree

5 files changed

+80
-28
lines changed

5 files changed

+80
-28
lines changed

Makefile

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
UNAME_S=$(shell uname -s)
12
EMACS_ROOT ?= ../..
2-
EMACS ?= emacs
3+
ifeq ($(UNAME_S),Darwin)
4+
EMACS ?= /Applications/Emacs.app/Contents/MacOS/Emacs
5+
else
6+
EMACS ?= emacs
7+
endif
38

49
CC = gcc
510
LD = gcc

README.md

+4-5
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,11 @@ Execute SQL `query` for `db` database.
4141
First argument `db` must be sqlite3 instance. If you use placeholders in `query`,
4242
then you must pass `bounds` too.
4343

44-
#### `(sqlite3-execute db query &optional callback)`
44+
#### `(sqlite3-execute db query &rest args)`
4545

46-
Interface for executing `SELECT` query. If you pass `callback` argument,
47-
`callback` is called with `SELECT` results. `callback` takes 2 arguments,
48-
`row` and `fields`. `row` is value, `fields` are column names. If `callback`
49-
is not specified, this function returns `resultset` instance.
46+
Interface for executing `SELECT` query.
47+
48+
Rest parameters are `bounds` and `callback`. You can its argument as, either '(bounds) or '(callback) or '(bounds callback). `callback` function is called with database row. `callback' takes two arguments, first argument is row element of list, second argument is field names of list.
5049

5150
#### `(sqlite3-resultset-next resultset)`
5251

sqlite3-core.c

+30-13
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ bind_values(emacs_env *env, sqlite3 *db, sqlite3_stmt *stmt, emacs_value bounds)
113113
if (eq_type(env, type, "string")) {
114114
ptrdiff_t size;
115115
const char *p = retrieve_string(env, bound, &size);
116-
ret = sqlite3_bind_text(stmt, i+1, p, size, NULL);
116+
ret = sqlite3_bind_text(stmt, i+1, p, size-1, NULL);
117117
} else if (eq_type(env, type, "integer")) {
118118
intmax_t num = env->extract_integer(env, bound);
119119
ret = sqlite3_bind_int64(stmt, i+1, num);
@@ -146,6 +146,7 @@ Fsqlite3_execute_batch(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void
146146
emacs_value Qnil = env->intern(env, "nil");
147147
emacs_value retval = Qnil;
148148
const char *errmsg = NULL;
149+
bool hasPlaceHolder = env->is_not_nil(env, args[2]);
149150

150151
char *top = malloc(size);
151152
if (top == NULL) {
@@ -156,10 +157,10 @@ Fsqlite3_execute_batch(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void
156157
memcpy(top, query, size);
157158
tail = top;
158159

159-
while (*(sql = tail)) {
160+
while (*(sql = tail) != '\0') {
160161
sqlite3_stmt *stmt = NULL;
161162
int ret = sqlite3_prepare_v2(sdb, sql, -1, &stmt, (const char**)&tail);
162-
if (nargs > 2) {
163+
if (hasPlaceHolder) {
163164
const char *err = bind_values(env, sdb, stmt, args[2]);
164165
if (err != NULL) {
165166
errmsg = err;
@@ -255,6 +256,9 @@ Fsqlite3_execute(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data
255256
sqlite3 *sdb = env->get_user_ptr(env, args[0]);
256257
ptrdiff_t size;
257258
char *query = retrieve_string(env, args[1], &size);
259+
emacs_value Qnil = env->intern(env, "nil");
260+
emacs_value retval = Qnil;
261+
const char *errmsg = NULL;
258262

259263
sqlite3_stmt *stmt = NULL;
260264
int ret = sqlite3_prepare_v2(sdb, query, size, &stmt, NULL);
@@ -263,12 +267,19 @@ Fsqlite3_execute(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data
263267
sqlite3_finalize(stmt);
264268
}
265269

266-
free(query);
267-
return env->intern(env, "nil");
270+
goto exit;
271+
}
272+
273+
if (env->is_not_nil(env, args[2])) {
274+
const char *err = bind_values(env, sdb, stmt, args[2]);
275+
if (err != NULL) {
276+
errmsg = err;
277+
goto exit;
278+
}
268279
}
269280

270281
emacs_value Qcons = env->intern(env, "cons");
271-
emacs_value fields = env->intern(env, "nil");
282+
emacs_value fields = Qnil;
272283
int count = sqlite3_column_count(stmt);
273284
for (int i = 0; i < count; ++i) {
274285
const char *name = sqlite3_column_name(stmt, i);
@@ -283,15 +294,16 @@ Fsqlite3_execute(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data
283294
emacs_value rargs[] = {fields};
284295
fields = env->funcall(env, Qreverse, 1, rargs);
285296

286-
emacs_value cb = args[2];
297+
emacs_value cb = args[3];
287298
if (!env->is_not_nil(env, cb)) {
288299
struct el_sql_resultset *result = malloc(sizeof(struct el_sql_resultset));
289300

290301
result->db = sdb;
291302
result->stmt = stmt;
292303
result->fields = fields;
293304
result->eof = false;
294-
return env->make_user_ptr(env, el_sqlite3_resultset_free, result);
305+
retval = env->make_user_ptr(env, el_sqlite3_resultset_free, result);
306+
goto exit;
295307
}
296308

297309
while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) {
@@ -302,11 +314,16 @@ Fsqlite3_execute(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data
302314
}
303315

304316
sqlite3_finalize(stmt);
305-
if (ret != SQLITE_OK && ret != SQLITE_DONE) {
306-
return env->intern(env, "nil");
317+
318+
exit:
319+
free(query);
320+
321+
if (errmsg != NULL) {
322+
emacs_value errstr = env->make_string(env, errmsg, strlen(errmsg));
323+
env->non_local_exit_signal(env, env->intern(env, "error"), errstr);
307324
}
308325

309-
return env->intern(env, "nil");
326+
return retval;
310327
}
311328

312329
static emacs_value
@@ -409,8 +426,8 @@ emacs_module_init(struct emacs_runtime *ert)
409426
bind_function (env, lsym, env->make_function(env, amin, amax, csym, doc, data))
410427

411428
DEFUN("sqlite3-core-new", Fsqlite3_new, 1, 1, NULL, NULL);
412-
DEFUN("sqlite3-core-execute-batch", Fsqlite3_execute_batch, 2, 3, NULL, NULL);
413-
DEFUN("sqlite3-core-execute", Fsqlite3_execute, 3, 3, NULL, NULL);
429+
DEFUN("sqlite3-core-execute-batch", Fsqlite3_execute_batch, 3, 3, NULL, NULL);
430+
DEFUN("sqlite3-core-execute", Fsqlite3_execute, 4, 4, NULL, NULL);
414431
DEFUN("sqlite3-transaction", Fsqlite3_transaction, 1, 1, NULL, NULL);
415432
DEFUN("sqlite3-commit", Fsqlite3_commit, 1, 1, NULL, NULL);
416433
DEFUN("sqlite3-rollback", Fsqlite3_rollback, 1, 1, NULL, NULL);

sqlite3.el

+21-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
;;; sqlite3.el --- sqlite3 binding of Emacs Lisp
22

3-
;; Copyright (C) 2016 by Syohei YOSHIDA
3+
;; Copyright (C) 2017 by Syohei YOSHIDA
44

55
;; Author: Syohei YOSHIDA <[email protected]>
66
;; URL: https://github.com/syohex/emacs-sqlite3
7+
;; Package-Requires: ((emacs "25"))
78
;; Version: 0.01
89

910
;; This program is free software; you can redistribute it and/or modify
@@ -37,20 +38,32 @@ into memory."
3738
(cl-assert (not (null sqlite)))
3839
(cl-assert (stringp query))
3940
(if (null bounds)
40-
(sqlite3-core-execute-batch sqlite query)
41+
(sqlite3-core-execute-batch sqlite query nil)
4142
(unless (vectorp bounds)
4243
(cl-assert (listp bounds))
4344
(setq bounds (vconcat bounds)))
4445
(sqlite3-core-execute-batch sqlite query bounds)))
4546

46-
(defun sqlite3-execute (sqlite query &optional callback)
47-
"Execute SQL `query' which has `SELECT' command. If `callback' argument is
48-
specified, `callback' function is called with database row. `callback' takes
49-
two arguments, first argument is row element of list, second argument is
50-
field names of list."
47+
(defun sqlite3-execute (sqlite query &rest args)
48+
"Execute SQL `query' which has `SELECT' command.
49+
50+
Rest parameters are `bounds' and `callback'. You can its argument as,
51+
either '(bounds) or '(callback) or '(bounds callback). `callback' function
52+
is called with database row. `callback' takes two arguments, first argument
53+
is row element of list, second argument is field names of list."
5154
(cl-assert (not (null sqlite)))
5255
(cl-assert (stringp query))
53-
(sqlite3-core-execute sqlite query callback))
56+
(cl-assert (<= (length args) 2))
57+
(let* ((rargs (reverse args))
58+
(callback (car rargs))
59+
bounds)
60+
(when (functionp callback)
61+
(setq rargs (cdr rargs)))
62+
(when rargs
63+
(setq bounds (car rargs)))
64+
(when (and bounds (not (vectorp bounds)))
65+
(setq bounds (vconcat bounds)))
66+
(sqlite3-core-execute sqlite query bounds callback)))
5467

5568
(provide 'sqlite3)
5669

test/test.el

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
;;; test-sqlite3.el --- Unit tests for sqlite3.el
22

3-
;; Copyright (C) 2016 by Syohei YOSHIDA
3+
;; Copyright (C) 2017 by Syohei YOSHIDA
44

55
;; Author: Syohei YOSHIDA <[email protected]>
66

@@ -89,4 +89,22 @@
8989
(sqlite3-resultset-next resultset) ;; last call
9090
(should (sqlite3-resultset-eof resultset)))))
9191

92+
(ert-deftest sqlite3-execute-select-with-placeholder ()
93+
"Execute SELECT query with callback"
94+
(let ((db (sqlite3-new)))
95+
(sqlite3-execute-batch db "CREATE TABLE sample(id integer primary key, name text);")
96+
(sqlite3-execute-batch db "INSERT INTO sample(name) values(\"Alice\");")
97+
(sqlite3-execute-batch db "INSERT INTO sample(name) values(\"Bob\");")
98+
(let ((rows 0))
99+
(sqlite3-execute
100+
db
101+
"SELECT name FROM sample WHERE name = ?"
102+
["Alice"]
103+
(lambda (row fields)
104+
(cl-incf rows)
105+
(should (member (car row) '("Alice")))))
106+
(should (= rows 1)))
107+
108+
(should-error (sqlite3-execute-batch db "SELECT *" (lambda (&rest _args)) 'not-null))))
109+
92110
;;; test-sqlite3.el ends here

0 commit comments

Comments
 (0)