Skip to content

Commit 06401ab

Browse files
authored
Add individual binds (#298)
Adds ability to set individual binds and re-implements `bind/3` using the new `bind_*` functions. And also deprecates `bind/3` in favor of `bind/2` since we don't need conn reference anymore.
1 parent 6edec07 commit 06401ab

File tree

6 files changed

+513
-210
lines changed

6 files changed

+513
-210
lines changed

c_src/sqlite3_nif.c

Lines changed: 131 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -448,194 +448,124 @@ exqlite_prepare(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
448448
}
449449

450450
static ERL_NIF_TERM
451-
bind(ErlNifEnv* env, const ERL_NIF_TERM arg, sqlite3_stmt* statement, int index)
451+
raise_badarg(ErlNifEnv* env, ERL_NIF_TERM term)
452452
{
453-
int rc;
454-
int the_int;
455-
ErlNifSInt64 the_long_int;
456-
double the_double;
457-
char the_atom[MAX_ATOM_LENGTH + 1];
458-
ErlNifBinary the_blob;
459-
int arity;
460-
const ERL_NIF_TERM* tuple;
461-
462-
if (enif_get_int64(env, arg, &the_long_int)) {
463-
rc = sqlite3_bind_int64(statement, index, the_long_int);
464-
if (rc == SQLITE_OK) {
465-
return make_atom(env, "ok");
466-
}
467-
468-
return enif_raise_exception(
469-
env,
470-
make_bind_error(
471-
env,
472-
make_message(env, "Failed to bind argument as 64 bit integer"),
473-
arg));
474-
}
453+
ERL_NIF_TERM badarg = enif_make_tuple2(env, make_atom(env, "badarg"), term);
454+
return enif_raise_exception(env, badarg);
455+
}
475456

476-
if (enif_get_int(env, arg, &the_int)) {
477-
rc = sqlite3_bind_int(statement, index, the_int);
478-
if (rc == SQLITE_OK) {
479-
return make_atom(env, "ok");
480-
}
457+
static ERL_NIF_TERM
458+
exqlite_reset(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
459+
{
460+
statement_t* statement;
461+
if (!enif_get_resource(env, argv[0], statement_type, (void**)&statement))
462+
return raise_badarg(env, argv[0]);
481463

482-
return enif_raise_exception(
483-
env,
484-
make_bind_error(
485-
env,
486-
make_message(env, "Failed to bind argument as integer"),
487-
arg));
488-
}
464+
sqlite3_reset(statement->statement);
465+
return make_atom(env, "ok");
466+
}
489467

490-
if (enif_get_double(env, arg, &the_double)) {
491-
rc = sqlite3_bind_double(statement, index, the_double);
492-
if (rc == SQLITE_OK) {
493-
return make_atom(env, "ok");
494-
}
468+
static ERL_NIF_TERM
469+
exqlite_bind_parameter_count(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
470+
{
495471

496-
return enif_raise_exception(
497-
env,
498-
make_bind_error(
499-
env,
500-
make_message(env, "Failed to bind argument as double"),
501-
arg));
502-
}
472+
statement_t* statement;
473+
if (!enif_get_resource(env, argv[0], statement_type, (void**)&statement))
474+
return raise_badarg(env, argv[0]);
503475

504-
if (enif_get_atom(env, arg, the_atom, sizeof(the_atom), ERL_NIF_LATIN1)) {
505-
if (0 == strcmp("undefined", the_atom) || 0 == strcmp("nil", the_atom)) {
506-
rc = sqlite3_bind_null(statement, index);
507-
if (rc == SQLITE_OK) {
508-
return make_atom(env, "ok");
509-
}
476+
int bind_parameter_count = sqlite3_bind_parameter_count(statement->statement);
477+
return enif_make_int(env, bind_parameter_count);
478+
}
510479

511-
return enif_raise_exception(
512-
env,
513-
make_bind_error(
514-
env,
515-
make_message(env, "Failed to bind argument as null"),
516-
arg));
517-
}
480+
static ERL_NIF_TERM
481+
exqlite_bind_text(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
482+
{
483+
statement_t* statement;
484+
if (!enif_get_resource(env, argv[0], statement_type, (void**)&statement))
485+
return raise_badarg(env, argv[0]);
518486

519-
rc = sqlite3_bind_text(statement, index, the_atom, strlen(the_atom), SQLITE_TRANSIENT);
520-
if (rc == SQLITE_OK) {
521-
return make_atom(env, "ok");
522-
}
487+
unsigned int idx;
488+
if (!enif_get_uint(env, argv[1], &idx))
489+
return raise_badarg(env, argv[1]);
523490

524-
return enif_raise_exception(
525-
env,
526-
make_bind_error(
527-
env,
528-
make_message(env, "Failed to bind argument as text"),
529-
arg));
530-
}
491+
ErlNifBinary text;
492+
if (!enif_inspect_binary(env, argv[2], &text))
493+
return raise_badarg(env, argv[2]);
531494

532-
if (enif_inspect_iolist_as_binary(env, arg, &the_blob)) {
533-
rc = sqlite3_bind_text(statement, index, (char*)the_blob.data, the_blob.size, SQLITE_TRANSIENT);
534-
if (rc == SQLITE_OK) {
535-
return make_atom(env, "ok");
536-
}
495+
int rc = sqlite3_bind_text(statement->statement, idx, (char*)text.data, text.size, SQLITE_TRANSIENT);
496+
return enif_make_int(env, rc);
497+
}
537498

538-
return enif_raise_exception(
539-
env,
540-
make_bind_error(
541-
env,
542-
make_message(env, "Failed to bind argument as text"),
543-
arg));
544-
}
499+
static ERL_NIF_TERM
500+
exqlite_bind_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
501+
{
502+
statement_t* statement;
503+
if (!enif_get_resource(env, argv[0], statement_type, (void**)&statement))
504+
return raise_badarg(env, argv[0]);
545505

546-
if (enif_get_tuple(env, arg, &arity, &tuple)) {
547-
if (arity != 2) {
548-
return enif_raise_exception(
549-
env,
550-
make_bind_error(
551-
env,
552-
make_message(env, "Failed to bind argument as blob"),
553-
arg));
554-
}
506+
unsigned int idx;
507+
if (!enif_get_uint(env, argv[1], &idx))
508+
return raise_badarg(env, argv[1]);
555509

556-
if (enif_get_atom(env, tuple[0], the_atom, sizeof(the_atom), ERL_NIF_LATIN1)) {
557-
if (0 == strcmp("blob", the_atom)) {
558-
if (enif_inspect_iolist_as_binary(env, tuple[1], &the_blob)) {
559-
rc = sqlite3_bind_blob(statement, index, the_blob.data, the_blob.size, SQLITE_TRANSIENT);
560-
if (rc == SQLITE_OK) {
561-
return make_atom(env, "ok");
562-
}
563-
564-
return enif_raise_exception(
565-
env,
566-
make_bind_error(
567-
env,
568-
make_message(env, "Failed to bind argument as blob"),
569-
arg));
570-
}
571-
}
572-
}
573-
}
510+
ErlNifBinary blob;
511+
if (!enif_inspect_binary(env, argv[2], &blob))
512+
return raise_badarg(env, argv[2]);
574513

575-
return enif_raise_exception(
576-
env,
577-
make_bind_error(
578-
env,
579-
make_message(env, "Failed to bind argument"),
580-
arg));
514+
int rc = sqlite3_bind_blob(statement->statement, idx, (char*)blob.data, blob.size, SQLITE_TRANSIENT);
515+
return enif_make_int(env, rc);
581516
}
582517

583-
///
584-
/// @brief Binds arguments to the sql statement
585-
///
586518
static ERL_NIF_TERM
587-
exqlite_bind(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
519+
exqlite_bind_integer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
588520
{
589-
assert(env);
521+
statement_t* statement;
522+
if (!enif_get_resource(env, argv[0], statement_type, (void**)&statement))
523+
return raise_badarg(env, argv[0]);
590524

591-
unsigned int parameter_count = 0;
592-
unsigned int argument_list_length = 0;
593-
connection_t* conn = NULL;
594-
statement_t* statement = NULL;
595-
ERL_NIF_TERM list;
596-
ERL_NIF_TERM head;
597-
ERL_NIF_TERM tail;
525+
unsigned int idx;
526+
if (!enif_get_uint(env, argv[1], &idx))
527+
return raise_badarg(env, argv[1]);
598528

599-
if (argc != 3) {
600-
return enif_make_badarg(env);
601-
}
602-
603-
if (!enif_get_resource(env, argv[0], connection_type, (void**)&conn)) {
604-
return make_error_tuple(env, "invalid_connection");
605-
}
606-
607-
if (!enif_get_resource(env, argv[1], statement_type, (void**)&statement)) {
608-
return make_error_tuple(env, "invalid_statement");
609-
}
529+
ErlNifSInt64 i;
530+
if (!enif_get_int64(env, argv[2], &i))
531+
return raise_badarg(env, argv[2]);
610532

611-
if (!enif_get_list_length(env, argv[2], &argument_list_length)) {
612-
return make_error_tuple(env, "bad_argument_list");
613-
}
533+
int rc = sqlite3_bind_int64(statement->statement, idx, i);
534+
return enif_make_int(env, rc);
535+
}
614536

615-
parameter_count = (unsigned int)sqlite3_bind_parameter_count(statement->statement);
616-
if (parameter_count != argument_list_length) {
617-
return make_error_tuple(env, "arguments_wrong_length");
618-
}
537+
static ERL_NIF_TERM
538+
exqlite_bind_float(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
539+
{
540+
statement_t* statement;
541+
if (!enif_get_resource(env, argv[0], statement_type, (void**)&statement))
542+
return raise_badarg(env, argv[0]);
619543

620-
sqlite3_reset(statement->statement);
544+
unsigned int idx;
545+
if (!enif_get_uint(env, argv[1], &idx))
546+
return raise_badarg(env, argv[1]);
621547

622-
list = argv[2];
623-
for (unsigned int i = 0; i < argument_list_length; i++) {
624-
enif_get_list_cell(env, list, &head, &tail);
625-
ERL_NIF_TERM result = bind(env, head, statement->statement, i + 1);
548+
double f;
549+
if (!enif_get_double(env, argv[2], &f))
550+
return raise_badarg(env, argv[2]);
626551

627-
// We are going to ignore this, we have to pass it.
628-
ERL_NIF_TERM reason;
552+
int rc = sqlite3_bind_double(statement->statement, idx, f);
553+
return enif_make_int(env, rc);
554+
}
629555

630-
// Bind will set an exception if anything happens during that phase.
631-
if (enif_has_pending_exception(env, &reason)) {
632-
return make_error_tuple(env, "failed_to_bind_argument");
633-
}
556+
static ERL_NIF_TERM
557+
exqlite_bind_null(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
558+
{
559+
statement_t* statement;
560+
if (!enif_get_resource(env, argv[0], statement_type, (void**)&statement))
561+
return raise_badarg(env, argv[0]);
634562

635-
list = tail;
636-
}
563+
unsigned int idx;
564+
if (!enif_get_uint(env, argv[1], &idx))
565+
return raise_badarg(env, argv[1]);
637566

638-
return make_atom(env, "ok");
567+
int rc = sqlite3_bind_null(statement->statement, idx);
568+
return enif_make_int(env, rc);
639569
}
640570

641571
static ERL_NIF_TERM
@@ -1272,6 +1202,38 @@ exqlite_interrupt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
12721202
return make_atom(env, "ok");
12731203
}
12741204

1205+
static ERL_NIF_TERM
1206+
exqlite_errmsg(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1207+
{
1208+
connection_t* conn;
1209+
statement_t* statement;
1210+
const char* msg;
1211+
1212+
if (enif_get_resource(env, argv[0], connection_type, (void**)&conn)) {
1213+
msg = sqlite3_errmsg(conn->db);
1214+
} else if (enif_get_resource(env, argv[0], statement_type, (void**)&statement)) {
1215+
msg = sqlite3_errmsg(sqlite3_db_handle(statement->statement));
1216+
} else {
1217+
return raise_badarg(env, argv[0]);
1218+
}
1219+
1220+
if (!msg)
1221+
return make_atom(env, "nil");
1222+
1223+
return make_binary(env, msg, strlen(msg));
1224+
}
1225+
1226+
static ERL_NIF_TERM
1227+
exqlite_errstr(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1228+
{
1229+
int rc;
1230+
if (!enif_get_int(env, argv[0], &rc))
1231+
return raise_badarg(env, argv[0]);
1232+
1233+
const char* msg = sqlite3_errstr(rc);
1234+
return make_binary(env, msg, strlen(msg));
1235+
}
1236+
12751237
//
12761238
// Most of our nif functions are going to be IO bounded
12771239
//
@@ -1282,7 +1244,13 @@ static ErlNifFunc nif_funcs[] = {
12821244
{"execute", 2, exqlite_execute, ERL_NIF_DIRTY_JOB_IO_BOUND},
12831245
{"changes", 1, exqlite_changes, ERL_NIF_DIRTY_JOB_IO_BOUND},
12841246
{"prepare", 2, exqlite_prepare, ERL_NIF_DIRTY_JOB_IO_BOUND},
1285-
{"bind", 3, exqlite_bind, ERL_NIF_DIRTY_JOB_IO_BOUND},
1247+
{"reset", 1, exqlite_reset, ERL_NIF_DIRTY_JOB_CPU_BOUND},
1248+
{"bind_parameter_count", 1, exqlite_bind_parameter_count},
1249+
{"bind_text", 3, exqlite_bind_text},
1250+
{"bind_blob", 3, exqlite_bind_blob},
1251+
{"bind_integer", 3, exqlite_bind_integer},
1252+
{"bind_float", 3, exqlite_bind_float},
1253+
{"bind_null", 2, exqlite_bind_null},
12861254
{"step", 2, exqlite_step, ERL_NIF_DIRTY_JOB_IO_BOUND},
12871255
{"multi_step", 3, exqlite_multi_step, ERL_NIF_DIRTY_JOB_IO_BOUND},
12881256
{"columns", 2, exqlite_columns, ERL_NIF_DIRTY_JOB_IO_BOUND},
@@ -1295,6 +1263,8 @@ static ErlNifFunc nif_funcs[] = {
12951263
{"set_update_hook", 2, exqlite_set_update_hook, ERL_NIF_DIRTY_JOB_IO_BOUND},
12961264
{"set_log_hook", 1, exqlite_set_log_hook, ERL_NIF_DIRTY_JOB_IO_BOUND},
12971265
{"interrupt", 1, exqlite_interrupt, ERL_NIF_DIRTY_JOB_IO_BOUND},
1266+
{"errmsg", 1, exqlite_errmsg},
1267+
{"errstr", 1, exqlite_errstr},
12981268
};
12991269

13001270
ERL_NIF_INIT(Elixir.Exqlite.Sqlite3NIF, nif_funcs, on_load, NULL, NULL, on_unload)

lib/exqlite/bind_error.ex

Lines changed: 0 additions & 18 deletions
This file was deleted.

lib/exqlite/connection.ex

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -652,12 +652,12 @@ defmodule Exqlite.Connection do
652652

653653
defp bind_params(%Query{ref: ref, statement: statement} = query, params, state)
654654
when ref != nil do
655-
case Sqlite3.bind(state.db, ref, params) do
656-
:ok ->
657-
{:ok, query}
658-
659-
{:error, reason} ->
660-
{:error, %Error{message: to_string(reason), statement: statement}, state}
655+
try do
656+
Sqlite3.bind(ref, params)
657+
rescue
658+
e -> {:error, %Error{message: Exception.message(e), statement: statement}, state}
659+
else
660+
:ok -> {:ok, query}
661661
end
662662
end
663663

0 commit comments

Comments
 (0)