mirror of
https://github.com/janet-lang/janet
synced 2024-12-25 07:50:27 +00:00
Many updates to the sqlite module.
This commit is contained in:
parent
363a17ff8c
commit
4e6fc341dc
8
Makefile
8
Makefile
@ -127,6 +127,14 @@ valtest: $(DST_TARGET)
|
|||||||
valgrind --leak-check=full -v ./$(DST_TARGET) test/suite0.dst
|
valgrind --leak-check=full -v ./$(DST_TARGET) test/suite0.dst
|
||||||
valgrind --leak-check=full -v ./$(DST_TARGET) test/suite1.dst
|
valgrind --leak-check=full -v ./$(DST_TARGET) test/suite1.dst
|
||||||
|
|
||||||
|
###################
|
||||||
|
##### Natives #####
|
||||||
|
###################
|
||||||
|
|
||||||
|
natives: $(DST_TARGET)
|
||||||
|
$(MAKE) -C natives/hello
|
||||||
|
$(MAKE) -j 8 -C natives/sqlite3
|
||||||
|
|
||||||
#################
|
#################
|
||||||
##### Other #####
|
##### Other #####
|
||||||
#################
|
#################
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
# IN THE SOFTWARE.
|
# IN THE SOFTWARE.
|
||||||
|
|
||||||
CFLAGS=-std=c99 -Wall -Wextra -I../../src/include -O2 -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -shared -fpic
|
CFLAGS=-std=c99 -Wall -Wextra -I../../src/include -O2 -shared -fpic \
|
||||||
|
-DSQLITE_THREADSAFE=0 \
|
||||||
|
-DSQLITE_OMIT_LOAD_EXTENSION
|
||||||
TARGET=sqlite3.so
|
TARGET=sqlite3.so
|
||||||
|
|
||||||
# MacOS specifics
|
# MacOS specifics
|
||||||
@ -27,19 +29,29 @@ ifeq ($(UNAME), Darwin)
|
|||||||
CFLAGS:=$(CFLAGS) -undefined dynamic_lookup
|
CFLAGS:=$(CFLAGS) -undefined dynamic_lookup
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
OBJECTS:=main.o sqlite3.o
|
||||||
|
$(TARGET): $(OBJECTS)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
sqlite-autoconf-3230100/sqlite3.%:
|
sqlite-autoconf-3230100/sqlite3.%:
|
||||||
curl https://www.sqlite.org/2018/sqlite-autoconf-3230100.tar.gz | tar -xvz
|
curl https://www.sqlite.org/2018/sqlite-autoconf-3230100.tar.gz | tar -xvz
|
||||||
|
|
||||||
sqlite3.%: sqlite-autoconf-3230100/sqlite3.%
|
sqlite3.c: sqlite-autoconf-3230100/sqlite3.c
|
||||||
|
cp $< $@
|
||||||
|
sqlite3.h: sqlite-autoconf-3230100/sqlite3.h
|
||||||
cp $< $@
|
cp $< $@
|
||||||
|
|
||||||
$(TARGET): main.c sqlite3.c sqlite3.h
|
%.o: %.c
|
||||||
$(CC) $(CFLAGS) -o $@ main.c
|
$(CC) $(CFLAGS) -c $<
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf sqlite-autoconf-3230100
|
rm -rf sqlite-autoconf-3230100
|
||||||
|
rm *.o
|
||||||
rm sqlite3.c
|
rm sqlite3.c
|
||||||
rm sqlite3.h
|
rm sqlite3.h
|
||||||
rm $(TARGET)
|
rm $(TARGET)
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean all
|
||||||
|
@ -20,17 +20,50 @@
|
|||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "sqlite3.c"
|
|
||||||
|
|
||||||
#include "sqlite3.h"
|
#include "sqlite3.h"
|
||||||
|
|
||||||
#include <dst/dst.h>
|
#include <dst/dst.h>
|
||||||
|
|
||||||
|
#define FLAG_CLOSED 1
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sqlite3* handle;
|
||||||
|
int flags;
|
||||||
|
} Db;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sqlite3_stmt* handle;
|
||||||
|
int flags;
|
||||||
|
} Stmt;
|
||||||
|
|
||||||
|
/* Close a db, noop if already closed */
|
||||||
|
static void closedb(Db *db) {
|
||||||
|
if (!(db->flags & FLAG_CLOSED)) {
|
||||||
|
db->flags |= FLAG_CLOSED;
|
||||||
|
sqlite3_close_v2(db->handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close prepared statement, noop if already closed */
|
||||||
|
static void closestmt(Stmt *stmt) {
|
||||||
|
if (!(stmt->flags & FLAG_CLOSED)) {
|
||||||
|
stmt->flags |= FLAG_CLOSED;
|
||||||
|
sqlite3_finalize(stmt->handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Called to garbage collect a sqlite3 connection */
|
/* Called to garbage collect a sqlite3 connection */
|
||||||
static int gcsqlite(void *p, size_t s) {
|
static int gcsqlite(void *p, size_t s) {
|
||||||
(void) s;
|
(void) s;
|
||||||
sqlite3 *conn = *((sqlite3 **)p);
|
Db *db = (Db *)p;
|
||||||
sqlite3_close_v2(conn);
|
closedb(db);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called to collect a sqlite3 prepared statement */
|
||||||
|
static int gcstmt(void *p, size_t s) {
|
||||||
|
(void) s;
|
||||||
|
Stmt *stmt = (Stmt *)p;
|
||||||
|
closestmt(stmt);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,77 +73,378 @@ static const DstAbstractType sql_conn_type = {
|
|||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const DstAbstractType sql_stmt_type = {
|
||||||
|
":sqlite3.statement",
|
||||||
|
gcstmt,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
/* Open a new database connection */
|
/* Open a new database connection */
|
||||||
static int sql_open(DstArgs args) {
|
static int sql_open(DstArgs args) {
|
||||||
sqlite3 **conn;
|
sqlite3 *conn;
|
||||||
const uint8_t *filename;
|
const uint8_t *filename;
|
||||||
int status;
|
int status;
|
||||||
DST_FIXARITY(args, 1);
|
DST_FIXARITY(args, 1);
|
||||||
DST_ARG_STRING(filename, args, 0);
|
DST_ARG_STRING(filename, args, 0);
|
||||||
conn = (sqlite3 **) dst_abstract(&sql_conn_type, sizeof(sqlite3 *));
|
status = sqlite3_open((const char *)filename, &conn);
|
||||||
status = sqlite3_open((const char *)filename, conn);
|
|
||||||
if (status == SQLITE_OK) {
|
if (status == SQLITE_OK) {
|
||||||
DST_RETURN_ABSTRACT(args, conn);
|
Db *db = (Db *) dst_abstract(&sql_conn_type, sizeof(Db));
|
||||||
|
db->handle = conn;
|
||||||
|
db->flags = 0;
|
||||||
|
DST_RETURN_ABSTRACT(args, db);
|
||||||
} else {
|
} else {
|
||||||
const char *err = sqlite3_errmsg(*conn);
|
const char *err = sqlite3_errmsg(conn);
|
||||||
DST_THROW(args, err);
|
DST_THROW(args, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Close a database connection */
|
||||||
static int sql_close(DstArgs args) {
|
static int sql_close(DstArgs args) {
|
||||||
sqlite3 **conn;
|
Db *db;
|
||||||
int status;
|
|
||||||
DST_FIXARITY(args, 1);
|
DST_FIXARITY(args, 1);
|
||||||
DST_CHECKABSTRACT(args, 0, &sql_conn_type);
|
DST_CHECKABSTRACT(args, 0, &sql_conn_type);
|
||||||
conn = (sqlite3 **)dst_unwrap_abstract(args.v[0]);
|
db = (Db *)dst_unwrap_abstract(args.v[0]);
|
||||||
status = sqlite3_close_v2(*conn);
|
closedb(db);
|
||||||
if (status == SQLITE_OK) {
|
DST_RETURN_NIL(args);
|
||||||
DST_RETURN_NIL(args);
|
|
||||||
} else {
|
|
||||||
DST_THROW(args, "unable to close the sqlite3 connection");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sql_execute_callback(void *rowsp, int n, char **vals, char **colnames) {
|
/* Check for embedded NULL bytes */
|
||||||
int i;
|
static int has_null(const uint8_t *str, int32_t len) {
|
||||||
DstArray *rows = (DstArray *)rowsp;
|
while (len--) {
|
||||||
DstKV *row = dst_struct_begin(n);
|
if (!str[len])
|
||||||
for (i = 0; i < n; i++) {
|
return 1;
|
||||||
dst_struct_put(row, dst_cstringv(colnames[i]), dst_cstringv(vals[i]));
|
|
||||||
}
|
}
|
||||||
dst_array_push(rows, dst_wrap_struct(dst_struct_end(row)));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sql_sql(DstArgs args) {
|
/* Create a prepared statement */
|
||||||
|
static int sql_prepare(DstArgs args) {
|
||||||
|
Db *db;
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
const uint8_t *zSql;
|
||||||
int status;
|
int status;
|
||||||
char *errmsg = "connection closed";
|
|
||||||
const uint8_t *str;
|
|
||||||
DstArray *rows;
|
|
||||||
sqlite3 **conn;
|
|
||||||
DST_FIXARITY(args, 2);
|
DST_FIXARITY(args, 2);
|
||||||
DST_CHECKABSTRACT(args, 0, &sql_conn_type);
|
DST_CHECKABSTRACT(args, 0, &sql_conn_type);
|
||||||
conn = (sqlite3 **)dst_unwrap_abstract(args.v[0]);
|
db = (Db *)dst_unwrap_abstract(args.v[0]);
|
||||||
DST_ARG_STRING(str, args, 1);
|
DST_ARG_STRING(zSql, args, 1);
|
||||||
rows = dst_array(10);
|
status = sqlite3_prepare_v2(db->handle, (const char *)zSql, -1, &stmt, NULL);
|
||||||
status = sqlite3_exec(
|
|
||||||
*conn,
|
|
||||||
(const char *)str,
|
|
||||||
sql_execute_callback,
|
|
||||||
rows,
|
|
||||||
&errmsg);
|
|
||||||
if (status == SQLITE_OK) {
|
if (status == SQLITE_OK) {
|
||||||
DST_RETURN_ARRAY(args, rows);
|
Stmt *sp = (Stmt *)dst_abstract(&sql_stmt_type, sizeof(Stmt));
|
||||||
} else {
|
sp->handle = stmt;
|
||||||
DST_THROW(args, errmsg);
|
sp->flags = 0;
|
||||||
|
DST_RETURN_ABSTRACT(args, sp);
|
||||||
}
|
}
|
||||||
|
DST_THROW(args, sqlite3_errmsg(db->handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finalize a prepared statement */
|
||||||
|
static int sql_finalize(DstArgs args) {
|
||||||
|
DST_FIXARITY(args, 1);
|
||||||
|
DST_CHECKABSTRACT(args, 0, &sql_stmt_type);
|
||||||
|
Stmt *stmt = (Stmt *) dst_unwrap_abstract(args.v[0]);
|
||||||
|
closestmt(stmt);
|
||||||
|
DST_RETURN_NIL(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bind a single parameter */
|
||||||
|
static const char *bind1(sqlite3_stmt *stmt, int index, Dst value) {
|
||||||
|
int res;
|
||||||
|
switch (dst_type(value)) {
|
||||||
|
default:
|
||||||
|
return "invalid sql value";
|
||||||
|
case DST_NIL:
|
||||||
|
res = sqlite3_bind_null(stmt, index);
|
||||||
|
break;
|
||||||
|
case DST_FALSE:
|
||||||
|
res = sqlite3_bind_int(stmt, index, 0);
|
||||||
|
break;
|
||||||
|
case DST_TRUE:
|
||||||
|
res = sqlite3_bind_int(stmt, index, 1);
|
||||||
|
break;
|
||||||
|
case DST_REAL:
|
||||||
|
res = sqlite3_bind_double(stmt, index, dst_unwrap_real(value));
|
||||||
|
break;
|
||||||
|
case DST_INTEGER:
|
||||||
|
res = sqlite3_bind_int64(stmt, index, dst_unwrap_integer(value));
|
||||||
|
break;
|
||||||
|
case DST_STRING:
|
||||||
|
case DST_SYMBOL:
|
||||||
|
{
|
||||||
|
const uint8_t *str = dst_unwrap_string(value);
|
||||||
|
int32_t len = dst_string_length(str);
|
||||||
|
if (has_null(str, len)) {
|
||||||
|
return "cannot have embedded nulls in text values";
|
||||||
|
} else {
|
||||||
|
res = sqlite3_bind_text(stmt, index, (const char *)str, len + 1, SQLITE_STATIC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DST_BUFFER:
|
||||||
|
{
|
||||||
|
DstBuffer *buffer = dst_unwrap_buffer(value);
|
||||||
|
res = sqlite3_bind_blob(stmt, index, buffer->data, buffer->count, SQLITE_STATIC);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (res != SQLITE_OK) {
|
||||||
|
sqlite3 *db = sqlite3_db_handle(stmt);
|
||||||
|
return sqlite3_errmsg(db);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bind many parameters */
|
||||||
|
static const char *bindmany(sqlite3_stmt *stmt, Dst params) {
|
||||||
|
/* parameters */
|
||||||
|
const Dst *seq;
|
||||||
|
const DstKV *kvs;
|
||||||
|
int32_t len, cap;
|
||||||
|
int limitindex = sqlite3_bind_parameter_count(stmt);
|
||||||
|
if (dst_seq_view(params, &seq, &len)) {
|
||||||
|
if (len > limitindex + 1) {
|
||||||
|
return "invalid index in sql parameters";
|
||||||
|
}
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
const char *err = bind1(stmt, i + 1, seq[i]);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (dst_hashtable_view(params, &kvs, &len, &cap)) {
|
||||||
|
for (int i = 0; i < cap; i++) {
|
||||||
|
int index = 0;
|
||||||
|
switch (dst_type(kvs[i].key)) {
|
||||||
|
default:
|
||||||
|
/* Will fail */
|
||||||
|
break;
|
||||||
|
case DST_NIL:
|
||||||
|
/* Will skip as nil keys indicate empty hash table slot */
|
||||||
|
continue;
|
||||||
|
case DST_INTEGER:
|
||||||
|
index = dst_unwrap_integer(kvs[i].key);
|
||||||
|
break;
|
||||||
|
case DST_STRING:
|
||||||
|
case DST_SYMBOL:
|
||||||
|
{
|
||||||
|
const uint8_t *s = dst_unwrap_string(kvs[i].key);
|
||||||
|
index = sqlite3_bind_parameter_index(
|
||||||
|
stmt,
|
||||||
|
(const char *)s);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (index <= 0 || index > limitindex) {
|
||||||
|
return "invalid index in sql parameters";
|
||||||
|
}
|
||||||
|
const char *err = bind1(stmt, index, kvs[i].value);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "invalid type for sql parameters";
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute a statement but don't collect results */
|
||||||
|
static const char *execute_drop(sqlite3_stmt *stmt) {
|
||||||
|
int status;
|
||||||
|
const char *ret = NULL;
|
||||||
|
do {
|
||||||
|
status = sqlite3_step(stmt);
|
||||||
|
} while (status == SQLITE_ROW);
|
||||||
|
/* Check for errors */
|
||||||
|
if (status != SQLITE_DONE) {
|
||||||
|
sqlite3 *db = sqlite3_db_handle(stmt);
|
||||||
|
ret = sqlite3_errmsg(db);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute and return values from prepared statement */
|
||||||
|
static const char *execute(sqlite3_stmt *stmt, DstArray *rows) {
|
||||||
|
/* Count number of columns in result */
|
||||||
|
int ncol = sqlite3_column_count(stmt);
|
||||||
|
int status;
|
||||||
|
const char *ret = NULL;
|
||||||
|
|
||||||
|
/* Get column names */
|
||||||
|
Dst *tupstart = dst_tuple_begin(ncol);
|
||||||
|
for (int i = 0; i < ncol; i++) {
|
||||||
|
tupstart[i] = dst_cstringv(sqlite3_column_name(stmt, i));
|
||||||
|
}
|
||||||
|
const Dst *colnames = dst_tuple_end(tupstart);
|
||||||
|
|
||||||
|
do {
|
||||||
|
status = sqlite3_step(stmt);
|
||||||
|
if (status == SQLITE_ROW) {
|
||||||
|
DstKV *row = dst_struct_begin(ncol);
|
||||||
|
for (int i = 0; i < ncol; i++) {
|
||||||
|
int t = sqlite3_column_type(stmt, i);
|
||||||
|
Dst value;
|
||||||
|
switch (t) {
|
||||||
|
case SQLITE_NULL:
|
||||||
|
value = dst_wrap_nil();
|
||||||
|
break;
|
||||||
|
case SQLITE_INTEGER:
|
||||||
|
value = dst_wrap_integer(sqlite3_column_int(stmt, i));
|
||||||
|
break;
|
||||||
|
case SQLITE_FLOAT:
|
||||||
|
value = dst_wrap_real(sqlite3_column_double(stmt, i));
|
||||||
|
break;
|
||||||
|
case SQLITE_TEXT:
|
||||||
|
{
|
||||||
|
int nbytes = sqlite3_column_bytes(stmt, i) - 1;
|
||||||
|
uint8_t *str = dst_string_begin(nbytes);
|
||||||
|
memcpy(str, sqlite3_column_text(stmt, i), nbytes);
|
||||||
|
value = dst_wrap_string(dst_string_end(str));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SQLITE_BLOB:
|
||||||
|
{
|
||||||
|
int nbytes = sqlite3_column_bytes(stmt, i);
|
||||||
|
DstBuffer *b = dst_buffer(nbytes);
|
||||||
|
memcpy(b->data, sqlite3_column_blob(stmt, i), nbytes);
|
||||||
|
b->count = nbytes;
|
||||||
|
value = dst_wrap_buffer(b);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dst_struct_put(row, colnames[i], value);
|
||||||
|
}
|
||||||
|
dst_array_push(rows, dst_wrap_struct(dst_struct_end(row)));
|
||||||
|
}
|
||||||
|
} while (status == SQLITE_ROW);
|
||||||
|
|
||||||
|
/* Check for errors */
|
||||||
|
if (status != SQLITE_DONE) {
|
||||||
|
sqlite3 *db = sqlite3_db_handle(stmt);
|
||||||
|
ret = sqlite3_errmsg(db);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run a prepared statement */
|
||||||
|
static int sql_exec(DstArgs args) {
|
||||||
|
DST_MINARITY(args, 1);
|
||||||
|
DST_MAXARITY(args, 2);
|
||||||
|
DST_CHECKABSTRACT(args, 0, &sql_stmt_type);
|
||||||
|
Stmt *stmt = (Stmt *)dst_unwrap_abstract(args.v[0]);
|
||||||
|
if (args.n == 2) {
|
||||||
|
const char *err = bindmany(stmt->handle, args.v[1]);
|
||||||
|
if (err) {
|
||||||
|
DST_THROW(args, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DstArray *rows = dst_array(10);
|
||||||
|
const char *err = execute(stmt->handle, rows);
|
||||||
|
/* Reset the statement */
|
||||||
|
sqlite3_reset(stmt->handle);
|
||||||
|
sqlite3_clear_bindings(stmt->handle);
|
||||||
|
if (err) {
|
||||||
|
DST_THROW(args, err);
|
||||||
|
}
|
||||||
|
DST_RETURN_ARRAY(args, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Evaluate a string of sql */
|
||||||
|
static int sql_eval(DstArgs args) {
|
||||||
|
const char *err;
|
||||||
|
sqlite3_stmt *stmt = NULL, *stmt_next = NULL;
|
||||||
|
const uint8_t *query;
|
||||||
|
|
||||||
|
DST_MINARITY(args, 2);
|
||||||
|
DST_MAXARITY(args, 3);
|
||||||
|
DST_CHECKABSTRACT(args, 0, &sql_conn_type);
|
||||||
|
Db *db = (Db *)dst_unwrap_abstract(args.v[0]);
|
||||||
|
DST_ARG_STRING(query, args, 1);
|
||||||
|
if (has_null(query, dst_string_length(query))) {
|
||||||
|
err = "cannot have embedded NULL in sql statememts";
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
DstArray *rows = dst_array(10);
|
||||||
|
const char *c = (const char *)query;
|
||||||
|
|
||||||
|
/* Evaluate all statements in a loop */
|
||||||
|
do {
|
||||||
|
/* Compile the next statement */
|
||||||
|
if (sqlite3_prepare_v2(db->handle, c, -1, &stmt_next, &c) != SQLITE_OK) {
|
||||||
|
err = sqlite3_errmsg(db->handle);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
/* Check if we have found last statement */
|
||||||
|
if (NULL == stmt_next) {
|
||||||
|
/* Execute current statement and collect results */
|
||||||
|
if (stmt) {
|
||||||
|
err = execute(stmt, rows);
|
||||||
|
if (err) goto error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Execute and finalize current statement */
|
||||||
|
if (stmt) {
|
||||||
|
err = execute_drop(stmt);
|
||||||
|
if (err) goto error;
|
||||||
|
}
|
||||||
|
/* Bind params to next statement*/
|
||||||
|
if (args.n == 3) {
|
||||||
|
/* parameters */
|
||||||
|
err = bindmany(stmt_next, args.v[2]);
|
||||||
|
if (err) goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* rotate stmt and stmt_next */
|
||||||
|
if (stmt) sqlite3_finalize(stmt);
|
||||||
|
stmt = stmt_next;
|
||||||
|
stmt_next = NULL;
|
||||||
|
} while (NULL != stmt);
|
||||||
|
|
||||||
|
/* Good return path */
|
||||||
|
DST_RETURN_ARRAY(args, rows);
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (stmt) sqlite3_finalize(stmt);
|
||||||
|
if (stmt_next) sqlite3_finalize(stmt_next);
|
||||||
|
DST_THROW(args, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert int64_t to a string */
|
||||||
|
static const uint8_t *coerce_int64(int64_t x) {
|
||||||
|
uint8_t bytes[40];
|
||||||
|
int i = 0;
|
||||||
|
/* Edge cases */
|
||||||
|
if (x == 0) return dst_cstring("0");
|
||||||
|
if (x == INT64_MIN) return dst_cstring("-9,223,372,036,854,775,808");
|
||||||
|
/* Negative becomes pos */
|
||||||
|
if (x < 0) {
|
||||||
|
bytes[i++] = '-';
|
||||||
|
x = -x;
|
||||||
|
}
|
||||||
|
while (x) {
|
||||||
|
bytes[i++] = x % 10;
|
||||||
|
x = x / 10;
|
||||||
|
}
|
||||||
|
bytes[i++] = '\0';
|
||||||
|
return dst_string(bytes, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gets the last inserted row id */
|
||||||
|
static int sql_last_insert_rowid(DstArgs args) {
|
||||||
|
DST_FIXARITY(args, 1);
|
||||||
|
DST_CHECKABSTRACT(args, 0, &sql_conn_type);
|
||||||
|
Db *db = (Db *)dst_unwrap_abstract(args.v[0]);
|
||||||
|
sqlite3_int64 id = sqlite3_last_insert_rowid(db->handle);
|
||||||
|
if (id >= INT32_MIN && id <= INT32_MAX) {
|
||||||
|
DST_RETURN_INTEGER(args, (int32_t) id);
|
||||||
|
}
|
||||||
|
/* Convert to string */
|
||||||
|
DST_RETURN_STRING(args, coerce_int64(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
static int sql_tables(DstArgs args) {}
|
|
||||||
static int sql_changes(DstArgs args) {}
|
static int sql_changes(DstArgs args) {}
|
||||||
static int sql_timeout(DstArgs args) {}
|
static int sql_timeout(DstArgs args) {}
|
||||||
static int sql_rowid(DstArgs args) {}
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -118,8 +452,11 @@ static int sql_rowid(DstArgs args) {}
|
|||||||
static const DstReg cfuns[] = {
|
static const DstReg cfuns[] = {
|
||||||
{"open", sql_open},
|
{"open", sql_open},
|
||||||
{"close", sql_close},
|
{"close", sql_close},
|
||||||
{"sql", sql_sql},
|
{"eval", sql_eval},
|
||||||
/*{"tables", sql_tables},*/
|
{"prepare", sql_prepare},
|
||||||
|
{"exec", sql_exec},
|
||||||
|
{"finalize", sql_finalize},
|
||||||
|
{"last-insert-rowid", sql_last_insert_rowid},
|
||||||
/*{"changes", sql_changes},*/
|
/*{"changes", sql_changes},*/
|
||||||
/*{"timeout", sql_timeout},*/
|
/*{"timeout", sql_timeout},*/
|
||||||
/*{"rowid", sql_rowid},*/
|
/*{"rowid", sql_rowid},*/
|
||||||
|
@ -1093,7 +1093,7 @@ returned from compiling and running the file."
|
|||||||
|
|
||||||
(def cache @{})
|
(def cache @{})
|
||||||
(def loading @{})
|
(def loading @{})
|
||||||
(fn [path args]
|
(fn require [path args]
|
||||||
(when (get loading path)
|
(when (get loading path)
|
||||||
(error (string "circular dependency: module " path " is loading")))
|
(error (string "circular dependency: module " path " is loading")))
|
||||||
(def {
|
(def {
|
||||||
|
@ -127,7 +127,7 @@ extern "C" {
|
|||||||
/* What to do when out of memory */
|
/* What to do when out of memory */
|
||||||
#ifndef DST_OUT_OF_MEMORY
|
#ifndef DST_OUT_OF_MEMORY
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#define DST_OUT_OF_MEMORY do { printf("out of memory\n"); exit(1); } while (0)
|
#define DST_OUT_OF_MEMORY do { printf("dst out of memory\n"); exit(1); } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Helper for debugging */
|
/* Helper for debugging */
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<title>Dst</title>
|
|
||||||
<style type="text/css" media="screen">
|
|
||||||
/* http://meyerweb.com/eric/tools/css/reset/
|
|
||||||
v2.0 | 20110126
|
|
||||||
License: none (public domain)
|
|
||||||
*/
|
|
||||||
|
|
||||||
html, body, div, span, applet, object, iframe,
|
|
||||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
|
||||||
a, abbr, acronym, address, big, cite, code,
|
|
||||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
|
||||||
small, strike, strong, sub, sup, tt, var,
|
|
||||||
b, u, i, center,
|
|
||||||
dl, dt, dd, ol, ul, li,
|
|
||||||
fieldset, form, label, legend,
|
|
||||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
|
||||||
article, aside, canvas, details, embed,
|
|
||||||
figure, figcaption, footer, header, hgroup,
|
|
||||||
menu, nav, output, ruby, section, summary,
|
|
||||||
time, mark, audio, video {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
font: inherit;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
/* HTML5 display-role reset for older browsers */
|
|
||||||
article, aside, details, figcaption, figure,
|
|
||||||
footer, header, hgroup, menu, nav, section {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
ol, ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
blockquote, q {
|
|
||||||
quotes: none;
|
|
||||||
}
|
|
||||||
blockquote:before, blockquote:after,
|
|
||||||
q:before, q:after {
|
|
||||||
content: '';
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#prompt {
|
|
||||||
white-space: nowrap;
|
|
||||||
width: calc(100% - 14px);
|
|
||||||
overflow: hidden;
|
|
||||||
font-family: monospace;
|
|
||||||
border: solid 2px black;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
#prompt br {
|
|
||||||
display:none;
|
|
||||||
}
|
|
||||||
#prompt * {
|
|
||||||
display:inline;
|
|
||||||
white-space:nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#console {
|
|
||||||
border: solid 2px black;
|
|
||||||
width: calc(100% - 4px);
|
|
||||||
min-height: 200px;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<pre id="console"></pre>
|
|
||||||
<div contenteditable="true" id="prompt"></div>
|
|
||||||
<script>
|
|
||||||
var con = document.getElementById('console');
|
|
||||||
var Module = {
|
|
||||||
|
|
||||||
preRun: function() {
|
|
||||||
var input = "";
|
|
||||||
var i = 0;
|
|
||||||
function stdin() {
|
|
||||||
if (input === "") {
|
|
||||||
input = prompt('Input:');
|
|
||||||
}
|
|
||||||
if (i < input.length) {
|
|
||||||
var code = input.charCodeAt(i);
|
|
||||||
++i;
|
|
||||||
return code;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function appendChunk(chunk) {
|
|
||||||
con.innerText += chunk + '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
var outbuf = "";
|
|
||||||
function stdout(asciiCode) {
|
|
||||||
if (asciiCode === 10) {
|
|
||||||
appendChunk(outbuf);
|
|
||||||
outbuf = '';
|
|
||||||
} else if (asciiCode === null) {
|
|
||||||
appendChunk(outbuf);
|
|
||||||
outbuf = ' ... ';
|
|
||||||
} else {
|
|
||||||
outbuf += String.fromCharCode(asciiCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var errbuf = "";
|
|
||||||
function stderr(asciiCode) {
|
|
||||||
if (asciiCode == 10) {
|
|
||||||
appendChunk(errbuf);
|
|
||||||
errbuf = '';
|
|
||||||
} else if (asciiCode === null) {
|
|
||||||
appendChunk(errbuf);
|
|
||||||
errbuf = ' ... ';
|
|
||||||
} else {
|
|
||||||
errbuf += String.fromCharCode(asciiCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FS.init(stdin, stdout, stderr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="dst.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018 Calvin Rose
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to
|
|
||||||
* deal in the Software without restriction, including without limitation the
|
|
||||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
* sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
* IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <dst/dst.h>
|
|
||||||
#include <dst/dstcompile.h>
|
|
||||||
|
|
||||||
// emscripten client code to go here
|
|
Loading…
Reference in New Issue
Block a user