diff options
author | Florian Dold <florian@dold.me> | 2023-08-04 18:12:11 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2023-08-10 15:23:47 +0200 |
commit | 5e69b78fe469fce5e4e83d5654d1f31334c3cbbe (patch) | |
tree | dbc3f3efa498ea6ed8d3e2cb051dc9d6256a1da6 | |
parent | 629c480cfd83e7b70f2d2de1bba35c0aa2d8c6da (diff) | |
download | quickjs-tart-5e69b78fe469fce5e4e83d5654d1f31334c3cbbe.tar.gz quickjs-tart-5e69b78fe469fce5e4e83d5654d1f31334c3cbbe.tar.bz2 quickjs-tart-5e69b78fe469fce5e4e83d5654d1f31334c3cbbe.zip |
sqlite3 support WIP
-rw-r--r-- | tart_module.c | 313 | ||||
-rw-r--r-- | test_sqlite3.js | 7 |
2 files changed, 247 insertions, 73 deletions
diff --git a/tart_module.c b/tart_module.c index 77e3b5b..314953d 100644 --- a/tart_module.c +++ b/tart_module.c @@ -1534,41 +1534,41 @@ static JSValue js_sqlite3_finalize(JSContext *ctx, JSValue this_val, } static int sql_exec_cb(void *cls, int numcol, char **res, char **colnames) { - printf("got row with %d columns\n", numcol); - return 0; + printf("got row with %d columns\n", numcol); + return 0; } static JSValue js_sqlite3_exec(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { - sqlite3 *sqlite3_db; - sqlite3_stmt *stmt; - JSValue db_handle = argv[0]; - JSValue stmt_str = argv[1]; - const char *stmt_cstr; - int res; - char *errmsg = NULL; - JSValue ret_val = JS_UNDEFINED; + sqlite3 *sqlite3_db; + sqlite3_stmt *stmt; + JSValue db_handle = argv[0]; + JSValue stmt_str = argv[1]; + const char *stmt_cstr; + int res; + char *errmsg = NULL; + JSValue ret_val = JS_UNDEFINED; - sqlite3_db = JS_GetOpaque(db_handle, js_sqlite3_database_class_id); - if (!sqlite3_db) { - ret_val = JS_ThrowTypeError(ctx, "invalid sqlite3 database handle"); - goto done; - } - stmt_cstr = JS_ToCString(ctx, stmt_str); - if (!stmt_cstr) { - ret_val = JS_ThrowTypeError(ctx, "invalid prepared statement, string expected"); - goto done; - } - res = sqlite3_exec(sqlite3_db, stmt_cstr, &sql_exec_cb, NULL, &errmsg); - if (SQLITE_OK != res) { - // FIXME: throw! - printf("got error: %s\n", errmsg); - } + sqlite3_db = JS_GetOpaque(db_handle, js_sqlite3_database_class_id); + if (!sqlite3_db) { + ret_val = JS_ThrowTypeError(ctx, "invalid sqlite3 database handle"); + goto done; + } + stmt_cstr = JS_ToCString(ctx, stmt_str); + if (!stmt_cstr) { + ret_val = JS_ThrowTypeError(ctx, "invalid prepared statement, string expected"); + goto done; + } + res = sqlite3_exec(sqlite3_db, stmt_cstr, &sql_exec_cb, NULL, &errmsg); + if (SQLITE_OK != res) { + // FIXME: throw! + printf("got error: %s\n", errmsg); + } done: - sqlite3_free(errmsg); - JS_FreeCString(ctx, stmt_cstr); - return JS_UNDEFINED; + sqlite3_free(errmsg); + JS_FreeCString(ctx, stmt_cstr); + return JS_UNDEFINED; } static int find_param_index(sqlite3_stmt *stmt, const char *name) @@ -1626,10 +1626,24 @@ static int bind_from_object(JSContext *ctx, sqlite3_stmt *stmt, JSValueConst obj JS_ThrowTypeError(ctx, "unable to bind, named param '%s' not found", key); goto fail; } - // FIXME: check if valid parameter! if (JS_IsNull(val)) { sqlite3_bind_null(stmt, param_index); + continue; } + if (JS_IsString(val)) { + const char *cstr; + cstr = JS_ToCString(ctx, val); + sqlite3_bind_text(stmt, param_index, cstr, strlen(cstr), SQLITE_TRANSIENT); + continue; + } + if (JS_IsNumber(val)) { + int64_t n; + JS_ToInt64(ctx, &n, val); + sqlite3_bind_int64(stmt, param_index, n); + continue; + } + JS_ThrowTypeError(ctx, "unable to bind, unsupported type"); + goto fail; } done: JS_FreeCString(ctx, key); @@ -1647,66 +1661,221 @@ fail: static JSValue js_sqlite3_stmt_run(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { - JSValue ret_val = JS_UNDEFINED; - JSValue stmt_handle = argv[0]; - sqlite3_stmt *stmt; - sqlite3 *db; - int sqlret; - - stmt = JS_GetOpaque(stmt_handle, js_sqlite3_statement_class_id); - if (!stmt) { - ret_val = JS_ThrowTypeError(ctx, "invalid sqlite3 database handle"); - goto done; - } - db = sqlite3_db_handle(stmt); - sqlret = sqlite3_reset(stmt); - if (SQLITE_OK != sqlret) { - ret_val = JS_ThrowTypeError(ctx, "failed to reset"); - goto done; - } - sqlret = sqlite3_clear_bindings(stmt); - if (SQLITE_OK != sqlret) { - ret_val = JS_ThrowTypeError(ctx, "failed to clear bindings"); - goto done; - } - if (argc > 1) { - if (0 != bind_from_object(ctx, stmt, argv[1])) { - ret_val = JS_EXCEPTION; + JSValue ret_val = JS_UNDEFINED; + JSValue stmt_handle = argv[0]; + sqlite3_stmt *stmt; + sqlite3 *db; + int sqlret; + + stmt = JS_GetOpaque(stmt_handle, js_sqlite3_statement_class_id); + if (!stmt) { + ret_val = JS_ThrowTypeError(ctx, "invalid sqlite3 database handle"); goto done; } - } - while (1) { - sqlret = sqlite3_step(stmt); - switch (sqlret) { - case SQLITE_ROW: - break; - case SQLITE_DONE: { - JSValue rowid_val; - ret_val = JS_NewObject(ctx); - sqlite3_int64 rowid = sqlite3_last_insert_rowid(db); - rowid_val = JS_NewBigUint64(ctx, rowid); - JS_SetPropertyStr(ctx, ret_val, "lastInsertRowid", rowid_val); + db = sqlite3_db_handle(stmt); + sqlret = sqlite3_reset(stmt); + if (SQLITE_OK != sqlret) { + ret_val = JS_ThrowTypeError(ctx, "failed to reset"); goto done; } - default: - ret_val = JS_ThrowTypeError(ctx, "sqlite3_step failed"); + sqlret = sqlite3_clear_bindings(stmt); + if (SQLITE_OK != sqlret) { + ret_val = JS_ThrowTypeError(ctx, "failed to clear bindings"); goto done; } - } + if (argc > 1) { + if (0 != bind_from_object(ctx, stmt, argv[1])) { + ret_val = JS_EXCEPTION; + goto done; + } + } + while (1) { + sqlret = sqlite3_step(stmt); + switch (sqlret) { + case SQLITE_ROW: + break; + case SQLITE_DONE: { + JSValue rowid_val; + ret_val = JS_NewObject(ctx); + sqlite3_int64 rowid = sqlite3_last_insert_rowid(db); + rowid_val = JS_NewBigUint64(ctx, rowid); + JS_SetPropertyStr(ctx, ret_val, "lastInsertRowid", rowid_val); + goto done; + } + default: + ret_val = JS_ThrowTypeError(ctx, "sqlite3_step failed"); + goto done; + } + } done: - return ret_val; + return ret_val; +} + +#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) +#define MIN_SAFE_INTEGER (-(((int64_t)1 << 53) - 1)) + + +static int extract_result_row(JSContext *ctx, sqlite3_stmt *stmt, JSValueConst target) +{ + int colcount; + int i; + + colcount = sqlite3_column_count(stmt); + + for (i = 0; i < colcount; i++) { + const char *colname = sqlite3_column_name(stmt, i); + int coltype = sqlite3_column_type(stmt, i); + switch (coltype) { + case SQLITE_INTEGER: { + int64_t val = sqlite3_column_int64(stmt, i); + if (val > MAX_SAFE_INTEGER || val < MIN_SAFE_INTEGER) { + JS_SetPropertyStr(ctx, target, colname, JS_NewBigInt64(ctx, val)); + } else { + JS_SetPropertyStr(ctx, target, colname, JS_NewInt64(ctx, val)); + } + break; + } + case SQLITE_FLOAT: { + double val = sqlite3_column_double(stmt, i); + JS_SetPropertyStr(ctx, target, colname, JS_NewFloat64(ctx, val)); + break; + } + case SQLITE_BLOB: + JS_ThrowInternalError(ctx, "blob not yet supported"); + return -1; + case SQLITE_NULL: + JS_SetPropertyStr(ctx, target, colname, JS_NULL); + break; + case SQLITE_TEXT: + const char *text = sqlite3_column_text(stmt, i); + JS_SetPropertyStr(ctx, target, colname, JS_NewString(ctx, text)); + break; + default: + JS_ThrowInternalError(ctx, "unexpected type from DB"); + return -1; + } + } + return 0; } static JSValue js_sqlite3_stmt_get_all(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { - return JS_UNDEFINED; + JSValue ret_val = JS_UNDEFINED; + JSValue stmt_handle = argv[0]; + sqlite3_stmt *stmt; + sqlite3 *db; + int sqlret; + JSValue rows_array = JS_UNDEFINED; + + stmt = JS_GetOpaque(stmt_handle, js_sqlite3_statement_class_id); + if (!stmt) { + ret_val = JS_ThrowTypeError(ctx, "invalid sqlite3 database handle"); + goto done; + } + db = sqlite3_db_handle(stmt); + sqlret = sqlite3_reset(stmt); + if (SQLITE_OK != sqlret) { + ret_val = JS_ThrowTypeError(ctx, "failed to reset"); + goto done; + } + sqlret = sqlite3_clear_bindings(stmt); + if (SQLITE_OK != sqlret) { + ret_val = JS_ThrowTypeError(ctx, "failed to clear bindings"); + goto done; + } + if (argc > 1) { + if (0 != bind_from_object(ctx, stmt, argv[1])) { + ret_val = JS_EXCEPTION; + goto done; + } + } + rows_array = JS_NewArray(ctx); + while (1) { + sqlret = sqlite3_step(stmt); + switch (sqlret) { + case SQLITE_ROW: { + JSValue row_obj = JS_NewObject(ctx); + if (0 != extract_result_row(ctx, stmt, row_obj)) { + goto fail; + } + qjs_array_append_new(ctx, rows_array, row_obj); + break; + } + case SQLITE_DONE: { + ret_val = JS_DupValue(ctx, rows_array); + goto done; + } + default: + ret_val = JS_ThrowTypeError(ctx, "sqlite3_step failed"); + goto done; + } + } +done: + JS_FreeValue(ctx, rows_array); + return ret_val; +fail: + ret_val = JS_EXCEPTION; + goto done; } static JSValue js_sqlite3_stmt_get_first(JSContext *ctx, JSValue this_val, int argc, JSValueConst *argv) { - return JS_UNDEFINED; + JSValue ret_val = JS_UNDEFINED; + JSValue stmt_handle = argv[0]; + sqlite3_stmt *stmt; + sqlite3 *db; + int sqlret; + + stmt = JS_GetOpaque(stmt_handle, js_sqlite3_statement_class_id); + if (!stmt) { + ret_val = JS_ThrowTypeError(ctx, "invalid sqlite3 database handle"); + goto done; + } + db = sqlite3_db_handle(stmt); + sqlret = sqlite3_reset(stmt); + if (SQLITE_OK != sqlret) { + ret_val = JS_ThrowTypeError(ctx, "failed to reset"); + goto done; + } + sqlret = sqlite3_clear_bindings(stmt); + if (SQLITE_OK != sqlret) { + ret_val = JS_ThrowTypeError(ctx, "failed to clear bindings"); + goto done; + } + if (argc > 1) { + if (0 != bind_from_object(ctx, stmt, argv[1])) { + ret_val = JS_EXCEPTION; + goto done; + } + } + while (1) { + sqlret = sqlite3_step(stmt); + switch (sqlret) { + case SQLITE_ROW: { + JSValue row_obj = JS_NewObject(ctx); + if (0 != extract_result_row(ctx, stmt, row_obj)) { + goto fail; + } + ret_val = row_obj; + goto done; + break; + } + case SQLITE_DONE: { + ret_val = JS_UNDEFINED; + goto done; + } + default: + ret_val = JS_ThrowTypeError(ctx, "sqlite3_step failed"); + goto done; + } + } +done: + return ret_val; +fail: + ret_val = JS_EXCEPTION; + goto done; } diff --git a/test_sqlite3.js b/test_sqlite3.js index 134863b..6de5cf1 100644 --- a/test_sqlite3.js +++ b/test_sqlite3.js @@ -15,4 +15,9 @@ console.log("stmt1 res:", res.lastInsertRowid); const stmt2 = tart.sqlite3Prepare(db, "select * from foo") res = tart.sqlite3StmtGetAll(stmt2); -console.log("result:", res); +console.log("result:", JSON.stringify(res)); + +const stmt3 = tart.sqlite3Prepare(db, "select * from foo") +res = tart.sqlite3StmtGetFirst(stmt3); + +console.log("result:", JSON.stringify(res)); |