/* Code generated by script */ #include "squirrel.h" #include "sqlite3.h" #include #include #include "sqstdblobimpl.h" typedef struct sq_sqlite3_sdb sq_sqlite3_sdb; typedef struct sq_sqlite3_sdb_func sq_sqlite3_sdb_func; /* to use as C user data so i know what function sqlite is calling */ struct sq_sqlite3_sdb_func { /* references to associated lua values */ HSQOBJECT fn_step; HSQOBJECT fn_finalize; HSQOBJECT udata; sq_sqlite3_sdb *sdb; sq_sqlite3_sdb_func *next; }; /* information about database */ struct sq_sqlite3_sdb { /* associated squirrel vm */ HSQUIRRELVM v; /* sqlite database handle */ sqlite3 *db; /* sql functions stack usage */ sq_sqlite3_sdb_func *func; /* top SQL function being called */ /* references */ HSQOBJECT busy_cb; /* busy callback */ HSQOBJECT busy_udata; HSQOBJECT progress_cb; /* progress handler */ HSQOBJECT progress_udata; HSQOBJECT trace_cb; /* trace callback */ HSQOBJECT trace_udata; HSQOBJECT null_value; }; SQ_OPT_STRING_STRLEN(); static const SQChar *SQLite3_TAG = "SQLite3"; static const SQChar *SQLite3_Stmt_TAG = "SQLite3Stmt"; static const SQChar sqlite3_NULL_Name[] = _SC("sqlite3_NULL"); static const SQChar nullName[] = _SC("Null"); static SQRESULT sqlite3_NULL_tostring(HSQUIRRELVM v){ sq_pushstring(v, "", 0); return 1; } SQRegFunction sqlite3_NULL_methods[]={ {_SC("_tostring"),sqlite3_NULL_tostring,1, _SC(".")}, {0,0} }; static SQRESULT get_sqlite3_instance(HSQUIRRELVM v, SQInteger idx, sq_sqlite3_sdb **sdb){ SQRESULT _rc_; if((_rc_ = sq_getinstanceup(v,idx,(SQUserPointer*)sdb,(void*)SQLite3_TAG)) < 0) return _rc_; if(!*sdb) return sq_throwerror(v, _SC("database is closed")); return _rc_; } static SQRESULT get_sqlite3_stmt_instance(HSQUIRRELVM v, SQInteger idx, sqlite3_stmt **stmt){ SQRESULT _rc_; if((_rc_ = sq_getinstanceup(v,idx,(SQUserPointer*)stmt,(void*)SQLite3_Stmt_TAG)) < 0) return _rc_; if(!*stmt) return sq_throwerror(v, _SC("statement is closed")); return _rc_; } //#define push_sqlite3_null(v) sq_getonregistrytable(v, sqlite3_NULL_Name, sizeof(sqlite3_NULL_Name)-1); //#define push_sqlite3_null(v) sq_pushobject(v, sqlite3_NULL); //#define push_sqlite3_null(v) sq_getbyname(v, 1, nullName, sizeof(nullName)-1) //#define push_sqlite3_null(v) sq_pushfromregistrytable(v, nullName, sizeof(nullName)-1) //#define push_sqlite3_null(v) {sq_pushregistrytable(v);sq_pushstring(v, sqlite3_NULL_Name, sizeof(sqlite3_NULL_Name)-1);sq_get(v, -2);sq_remove(v,-2);} //the line bellow is the fastest one, any other attempt till now was slower #define push_sqlite3_null(v) {sq_pushstring(v, nullName, sizeof(nullName)-1);sq_get(v, 1);} #define GET_sqlite3_INSTANCE_AT(idx) \ sq_sqlite3_sdb *sdb=NULL; \ if((_rc_ = get_sqlite3_instance(v,idx,&sdb)) < 0) return _rc_;\ sqlite3 *self = sdb->db; #define GET_sqlite3_INSTANCE() GET_sqlite3_INSTANCE_AT(1) //#define GET_sqlite3_INSTANCE() SQ_GET_INSTANCE(v, 1, sqlite3, SQLite3_TAG) #define GET_sqlite3_stmt_INSTANCE() \ sqlite3_stmt *self=NULL; \ if((_rc_ = get_sqlite3_stmt_instance(v,1,&self)) < 0) return _rc_; enum e_type_result {tr_first_row_first_col, tr_first_row, tr_all_rows, tr_ddml}; #define AS_STRING_ALWAYS 0x01 #define NULL_AS_EMPTY_STR 0x02 static void sqlite3_stmt_push_string(HSQUIRRELVM v, sqlite3_stmt *stmt, int col, int flags){ const char *value = (const char*) sqlite3_column_text(stmt, col); if(value) sq_pushstring(v, value, sqlite3_column_bytes(stmt, col)); else push_sqlite3_null(v); } static SQRESULT sqlite3_stmt_push_value(HSQUIRRELVM v, sqlite3_stmt *stmt, int col, int flags){ const char *value; if(flags & AS_STRING_ALWAYS) sqlite3_stmt_push_string(v, stmt, col, flags); else { switch (sqlite3_column_type(stmt, col)) { case SQLITE_INTEGER: { sqlite_int64 i64 = sqlite3_column_int64(stmt, col); SQInteger n = (SQInteger)i64; if (n == i64) sq_pushinteger(v, n); else sq_pushstring(v, (const char*) sqlite3_column_text(stmt, col), sqlite3_column_bytes(stmt, col)); } break; case SQLITE_FLOAT: sq_pushfloat(v, sqlite3_column_double(stmt, col)); break; case SQLITE_TEXT: sqlite3_stmt_push_string(v, stmt, col, flags); break; case SQLITE_BLOB: value = (const char*) sqlite3_column_blob(stmt, col); if(value) sq_pushstring(v, value, sqlite3_column_bytes(stmt, col)); else push_sqlite3_null(v); break; case SQLITE_NULL: push_sqlite3_null(v); break; default: push_sqlite3_null(v); break; } } return 1; } static void sqlite3_stmt_row_asArray(HSQUIRRELVM v, sqlite3_stmt *stmt, int flags){ int col_count = sqlite3_column_count(stmt); sq_newarray(v, col_count); for(int i=0; i params_start){ int nparams = sqlite3_bind_parameter_count(*stmt); int nparams_given = _top_-params_start; if(nparams != nparams_given){ return sq_throwerror(v, "expect %d parameters but only %d given", nparams, nparams_given); } for(int i=1; i <= nparams; ++i){ int argn = i+params_start; _rc_ = sqlite3_stmt_bind_value(v, *stmt, i, argn); if(_rc_ < 0) return _rc_; } } return SQ_OK; } static SQRESULT sq_sqlite3_stmt_releasehook(SQUserPointer p, SQInteger size, HSQUIRRELVM v) { sqlite3_stmt *stmt = ((sqlite3_stmt *)p); if(stmt) sqlite3_finalize(stmt); return 0; } static SQRESULT sq_sqlite3_stmt_constructor(HSQUIRRELVM v) { SQ_FUNC_VARS(v); _rc_ = SQ_ERROR; GET_sqlite3_INSTANCE_AT(2); sqlite3_stmt *stmt = 0; if(_top_ > 2){ _rc_ = sq_sqlite3_stmt_prepare_aux(v, self, &stmt, 3); } else { const char* szTail=0; if(sqlite3_prepare_v2(self, "select 'statement not prepared';", -1, &stmt, &szTail) != SQLITE_OK) { _rc_ = sq_throwerror(v, sqlite3_errmsg(self)); } else _rc_ = SQ_OK; } sq_setinstanceup(v, 1, stmt); //replace self for this instance with this new sqlite3_stmt sq_setreleasehook(v,1, sq_sqlite3_stmt_releasehook); return _rc_; } static SQRESULT sq_sqlite3_stmt_get_db(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); sqlite3 *db = sqlite3_db_handle(self); sq_pushuserpointer(v, db); if(sq_getonregistrytable(v) == SQ_OK){ sq_getweakrefval(v, -1); } else return sq_throwerror(v, _SC("could not retrive database, maybe it was already closed")); return 1; } static SQRESULT sq_sqlite3_stmt_stmt_ptr(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); sq_pushuserpointer(v, self); return 1; } static SQRESULT sq_sqlite3_stmt_finalize(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); if(sqlite3_finalize(self) == SQLITE_OK){ sq_setinstanceup(v, 1, 0); //next calls will fail with "statement is closed" } else { sqlite3 *db = sqlite3_db_handle(self); return sq_throwerror(v, sqlite3_errmsg(db)); } return 0; } static SQRESULT sq_sqlite3_stmt_prepare(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); sqlite3 *db = sqlite3_db_handle(self); sqlite3_stmt *stmt = 0; _rc_ = sq_sqlite3_stmt_prepare_aux(v, db, &stmt, 2); if(stmt){ sqlite3_finalize(self); //finalize the previous sqlite3_stmt sq_setinstanceup(v, 1, stmt); //replace self for this instance with this new sqlite3_stmt } return _rc_; } static SQRESULT sq_sqlite3_stmt_get_sql(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); sq_pushstring(v, sqlite3_sql(self), -1); return 1; } static SQRESULT sq_sqlite3_stmt_bind(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); SQ_GET_INTEGER(v, 2, npar); return sqlite3_stmt_bind_value(v, self, npar, 3); } static SQRESULT sq_sqlite3_stmt_bind_empty_null(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); SQ_GET_INTEGER(v, 2, npar); if(sq_gettype(v, 3) == OT_STRING && sq_getsize(v, 3) == 0){ sq_pushnull(v); sq_replace(v, 3); } return sqlite3_stmt_bind_value(v, self, npar, 3); } static SQRESULT sq_sqlite3_stmt_bind_blob(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); SQ_GET_INTEGER(v, 2, npar); SQ_GET_STRING(v, 3, blob); if(sqlite3_bind_blob(self, npar, blob, blob_size, SQLITE_TRANSIENT) != SQLITE_OK) { sqlite3 *db = sqlite3_db_handle(self); return sq_throwerror(v, sqlite3_errmsg(db)); } return SQ_OK; } static SQRESULT sq_sqlite3_stmt_bind_values(HSQUIRRELVM v){ SQ_FUNC_VARS(v); GET_sqlite3_stmt_INSTANCE(); if (_top_ - 1 != sqlite3_bind_parameter_count(self)) return sq_throwerror(v, _SC("incorrect number of parameters to bind (%d given, %d to bind)"), _top_ - 1, sqlite3_bind_parameter_count(self)); for (int n = 2; n <= _top_; ++n) { if (sqlite3_stmt_bind_value(v, self, n-1, n) != SQ_OK) return SQ_ERROR; } return 0; } static SQRESULT sq_sqlite3_stmt_bind_names(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); SQObjectType ptype = sq_gettype(v, 2); int count = sqlite3_bind_parameter_count(self); int result; for (int n = 1; n <= count; ++n) { const char *name = sqlite3_bind_parameter_name(self, n); if (name && (name[0] == ':' || name[0] == '$' || name[0] == '@')) { if(ptype == OT_TABLE){ sq_pushstring(v, ++name, -1); if(sq_get(v, 2) == SQ_OK){ result = sqlite3_stmt_bind_value(v, self, n, -1); sq_poptop(v); } else return sq_throwerror(v, _SC("bind parameter (%s) not found"), name); } else return sq_throwerror(v, _SC("table expected to bind named parameters")); } else { if(ptype == OT_ARRAY){ sq_pushinteger(v, n); if(sq_get(v, 2) == SQ_OK){ result = sqlite3_stmt_bind_value(v, self, n, -1); sq_poptop(v); } else return sq_throwerror(v, _SC("bind parameter (%d) not found"), n); } else return sq_throwerror(v, _SC("array expected to bind numbered parameters")); } if (result != SQ_OK) return result; } return 0; } static SQRESULT sq_sqlite3_stmt_bind_parameter_index(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); SQ_GET_STRING(v, 2, spar); sq_pushinteger(v, sqlite3_bind_parameter_index(self, spar)); return 1; } static SQRESULT sq_sqlite3_stmt_reset(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); sq_pushinteger(v, sqlite3_reset(self)); return 1; } static SQRESULT sq_sqlite3_stmt_step(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); sq_pushinteger(v, sqlite3_step(self)); return 1; } static SQRESULT sq_sqlite3_stmt_next_row(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); sq_pushbool(v, sqlite3_step(self) == SQLITE_ROW); return 1; } static SQRESULT sq_sqlite3_stmt_col_count(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); sq_pushinteger(v, sqlite3_column_count(self)); return 1; } static SQRESULT sq_sqlite3_stmt_colsAsArray(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); int col_count = sqlite3_column_count(self); sq_newarray(v, col_count); for(int i=0; i= SIZE1BYTE) { if(size == SIZE1BYTE) { //data bigger than 250 and less 500 next byte has more info size += *p++; } else if(size == SIZE2BYTE) { //data bigger than 250 next two bytes has more info size = (*p++ * IBYTE1); size += *p++; } else if(size == SIZE3BYTE) { //data bigger than 250 next three bytes has more info size = (*p++ * IBYTE2); size += (*p++ * IBYTE1); size += *p++; } else if(size == SIZE4BYTE) { //data bigger than 250 next four bytes has more info size = (*p++ * IBYTE3); size += (*p++ * IBYTE2); size += (*p++ * IBYTE1); size += *p++; } else if(size == SLEMARK) { //reserved can be used for multi part data, metadata, digest, ... break; } } sq_pushstring(v, (const char*)p, size); sq_arrayappend(v, -2); p += size; data_len += size; if(data_len > sle_size) { //something went wrong here return sq_throwerror(v, "We got more data than expected !"); } } *next = ++p; return 0; } static SQRESULT sle2vec(HSQUIRRELVM v, const unsigned char *sle, size_t sle_size, const unsigned char **next) { int rc; sq_newarray(v, 0); if(sle[0] != '[') { return sq_throwerror(v, "Invalid encoded vector !"); } if(sle[1] == ']') { *next = sle+2; //empty array return 0; } if((rc = sle2array(v, sle+1, sle_size-1, next)) < 0) return rc; if(!*next || *next[0] != ']'){ return sq_throwerror(v, "Invalid end of vector !"); } (*next)++; return 0; } static SQRESULT sq_sle2vecOfvec(HSQUIRRELVM v) { SQ_FUNC_VARS(v); SQBlob *blob = NULL; const SQChar *sle; SQInteger sle_size; SQObjectType ptype = sq_gettype(v, 2); switch(ptype){ case OT_STRING: sq_getstr_and_size(v, 2, &sle, &sle_size); break; case OT_INSTANCE: if(SQ_FAILED(sq_getinstanceup(v,2,(SQUserPointer*)&blob,(SQUserPointer)SQBlob::SQBlob_TAG))) return sq_throwerror(v,_SC("invalid type tag")); if(!blob || !blob->IsValid()) return sq_throwerror(v,_SC("the blob is invalid")); sle = (const SQChar*)blob->GetBuf(); sle_size = blob->Len(); break; default: return sq_throwerror(v,_SC("invalid parameter 1 of type %s"), sq_gettypename(v, 2)); } if(sq_gettype(v, 3) != OT_ARRAY) return sq_throwerror(v,_SC("invalid parameter 2 of type %s"), sq_gettypename(v, 3)); SQ_OPT_INTEGER(v, 4, start, 0); sq_push(v, 3); int rc, data_count = 1; const unsigned char *next; if(sle[0] != '[') { return sq_throwerror(v, "Invalid encoded vector !"); } if(sle[1] == ']') { //sq_pushinteger(v, start+2); return 1; //empty array } int saved_top = sq_gettop(v); if((rc=sle2vec(v, (const unsigned char *)sle+1, sle_size-1, &next)) < 0) return rc; while(next && *next == '['){ sq_arrayappend(v, -2); data_count++; if((rc=sle2vec(v, next, sle_size - (next - (const unsigned char *)sle), &next)) < 0) return rc; } if(sq_gettop(v) != saved_top){ //last added table left sq_arrayappend(v, -2); data_count++; } if(!next || *next != ']'){ return sq_throwerror(v, "Invalid end of vector !"); } sq_pushinteger(v, ((const SQChar*)(++next)) - sle); return 1; } typedef unsigned char slebuf_t[8]; static int sleSize2buf(slebuf_t sle, size_t size){ size_t next_size, tmp_size; if(size < SIZE1BYTE) { sle[0] = size; return 1; } else if(size < (SIZE1BYTE*2)) { sle[0] = SIZE1BYTE; next_size = size - 250; sle[1] = next_size; return 2; } else if(size < IBYTE2) { sle[0] = SIZE2BYTE; next_size = size/IBYTE1; sle[1] = next_size; next_size = size%IBYTE1; sle[2] = next_size; return 3; } else if(size < IBYTE3) { sle[0] = SIZE3BYTE; next_size = size/IBYTE2; sle[1] = next_size; tmp_size = size%IBYTE2; next_size = tmp_size/IBYTE1; sle[2] = next_size; next_size = tmp_size%IBYTE1; sle[3] = next_size; return 4; } else if(size < IBYTE4) { sle[0] = SIZE4BYTE; next_size = size/IBYTE3; sle[1] = next_size; tmp_size = size%IBYTE3; next_size = tmp_size/IBYTE2; sle[2] = next_size; tmp_size = tmp_size%IBYTE2; next_size = tmp_size/IBYTE1; sle[3] = next_size; next_size = tmp_size%IBYTE1; sle[5] = next_size; return 5; } return 0; } static SQRESULT sq_get_sle_size(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); slebuf_t slebuf; SQ_GET_INTEGER(v, 2, size); int slesize = sleSize2buf(slebuf, size); if(slesize) { sq_pushstring(v, (const char *)slebuf, slesize); return 1; } return 0; } static int add2sle(SQBlob &sle, const char *str, size_t size){ slebuf_t slebuf; int slesize = sleSize2buf(slebuf, size); if(slesize) sle.Write((const char *)slebuf, slesize); if(size) sle.Write(str, size); return size; } static SQRESULT sq_sqlite3_stmt_asSleArray(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_stmt_INSTANCE(); int col_count = sqlite3_column_count(self); int i; const char *value; SQBlob sle(0, 8192); sle.WriteZstr("[["); for(i=0; i < col_count; ++i){ value = sqlite3_column_name(self, i); add2sle(sle, value, strlen(value)); } sle.WriteChar(SLEEND); sle.WriteChar(']'); while(sqlite3_step(self) == SQLITE_ROW){ sle.WriteChar('['); for(i=0; i < col_count; ++i){ switch(sqlite3_column_type(self, i)) { case SQLITE_BLOB: { add2sle(sle, (const char*)sqlite3_column_blob(self, i), sqlite3_column_bytes(self, i)); } break; default: add2sle(sle, (const char*)sqlite3_column_text(self, i), sqlite3_column_bytes(self, i)); } } sle.WriteChar(SLEEND); sle.WriteChar(']'); } sle.WriteChar(']'); sq_pushstring(v, (const SQChar*)sle.GetBuf(), sle.Len()); return 1; } #define _DECL_FUNC(name,nparams,tycheck, isStatic) {_SC(#name), sq_sqlite3_stmt_##name,nparams,tycheck, isStatic} static SQRegFunction sq_sqlite3_stmt_methods[] = { _DECL_FUNC(constructor, -2, _SC("xxs s|n|b|o"), SQFalse), _DECL_FUNC(stmt_ptr, 1, _SC("x"), SQFalse), _DECL_FUNC(get_db, 1, _SC("x"), SQFalse), _DECL_FUNC(finalize, 1, _SC("x"), SQFalse), _DECL_FUNC(prepare, -2, _SC("xs s|n|b|o"), SQFalse), _DECL_FUNC(get_sql, 1, _SC("x"), SQFalse), _DECL_FUNC(bind, 3, _SC("xi s|n|b|o"), SQFalse), _DECL_FUNC(bind_empty_null, 3, _SC("xi s|n|b|o"), SQFalse), _DECL_FUNC(bind_blob, 3, _SC("xis"), SQFalse), _DECL_FUNC(bind_values, -2, _SC("x s|n|b|o"), SQFalse), _DECL_FUNC(bind_names, 2, _SC("x t|a"), SQFalse), _DECL_FUNC(bind_parameter_index, 2, _SC("xs"), SQFalse), _DECL_FUNC(step, 1, _SC("x"), SQFalse), _DECL_FUNC(reset, 1, _SC("x"), SQFalse), _DECL_FUNC(next_row, 1, _SC("x"), SQFalse), _DECL_FUNC(colsAsArray, 1, _SC("x"), SQFalse), _DECL_FUNC(colsAsTable, 1, _SC("x"), SQFalse), _DECL_FUNC(col_count, 1, _SC("x"), SQFalse), _DECL_FUNC(col_name, 2, _SC("xi"), SQFalse), _DECL_FUNC(col_type, 2, _SC("xi"), SQFalse), _DECL_FUNC(col_declared_type, 2, _SC("xi"), SQFalse), _DECL_FUNC(asArray, -1, _SC("xi"), SQFalse), _DECL_FUNC(asTable, -1, _SC("xi"), SQFalse), _DECL_FUNC(asArrayOfArrays, -1, _SC("xi"), SQFalse), _DECL_FUNC(asArrayOfTables, -1, _SC("xi"), SQFalse), _DECL_FUNC(asSleArray, 1, _SC("x"), SQFalse), _DECL_FUNC(col, 2, _SC("x i|s"), SQFalse), _DECL_FUNC(asString, 2, _SC("x i|s"), SQFalse), _DECL_FUNC(asStringOrNull, 2, _SC("x i|s"), SQFalse), _DECL_FUNC(asInteger, 2, _SC("x i|s"), SQFalse), _DECL_FUNC(asBool, 2, _SC("x i|s"), SQFalse), _DECL_FUNC(asFloat, 2, _SC("x i|s"), SQFalse), {0,0} }; #undef _DECL_FUNC static SQRESULT sqlite3_exec_fmt(HSQUIRRELVM v, e_type_result type_result, int null_as_empty_str=1){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); SQ_GET_STRING(v, 2, szSQL); sqlite3_stmt *stmt = 0; _rc_ = sq_sqlite3_stmt_prepare_aux(v, self, &stmt, 2); if(_rc_ < 0){ if(stmt) sqlite3_finalize(stmt); return _rc_; } int rc = sqlite3_step(stmt); if(type_result == tr_ddml) { if(rc != SQLITE_DONE) { sqlite3_finalize(stmt); return sq_throwerror(v, "SQL is not a DDML %d %s", rc, sqlite3_errmsg(self)); } sq_pushinteger(v, sqlite3_changes(sqlite3_db_handle(stmt))); } else { if(rc != SQLITE_ROW) { sqlite3_finalize(stmt); return sq_throwerror(v, "%d %s", rc, sqlite3_errmsg(self)); } switch(type_result){ case tr_first_row_first_col: sqlite3_stmt_push_value(v, stmt, 0, 0); break; case tr_first_row: sqlite3_stmt_row_asArray(v, stmt, NULL_AS_EMPTY_STR|AS_STRING_ALWAYS); break; case tr_all_rows: sqlite3_stmt_asArrayOfArrays(v, stmt, NULL_AS_EMPTY_STR|AS_STRING_ALWAYS); break; default: sqlite3_finalize(stmt); return sq_throwerror(v, "Unknown requested return type %d", type_result); } } sqlite3_finalize(stmt); return 1; } static SQRESULT sq_sqlite3_close_release(HSQUIRRELVM v, sq_sqlite3_sdb *sdb){ SQRESULT rc = SQ_ERROR; if(sdb){ if(sqlite3_close_v2(sdb->db) == SQLITE_OK){ rc = SQ_OK; //remove waekref from registrytable sq_pushregistrytable(sdb->v); sq_pushuserpointer(sdb->v, sdb->db); sq_deleteslot(sdb->v, -2, SQFalse); sq_poptop(sdb->v); if(sdb->func){ sq_sqlite3_sdb_func *func, *func_next; func = sdb->func; while (func) { func_next = func->next; sq_release(sdb->v, &func->fn_step); sq_release(sdb->v, &func->fn_finalize); sq_release(sdb->v, &func->udata); sq_free(func, sizeof(sq_sqlite3_sdb_func)); func = func_next; } sdb->func = NULL; } /* 'free' all references */ sq_release(sdb->v, &sdb->busy_cb); sq_release(sdb->v, &sdb->busy_udata); sq_release(sdb->v, &sdb->progress_cb); sq_release(sdb->v, &sdb->progress_udata); sq_release(sdb->v, &sdb->trace_cb); sq_release(sdb->v, &sdb->trace_udata); sq_release(sdb->v, &sdb->null_value); sq_free(sdb, sizeof(sq_sqlite3_sdb)); } } return rc; } static SQRESULT sq_sqlite3_releasehook(SQUserPointer p, SQInteger size, HSQUIRRELVM v) { sq_sqlite3_sdb *sdb = ((sq_sqlite3_sdb *)p); sq_sqlite3_close_release(v, sdb); return 0; } static SQRESULT sq_sqlite3_constructor(HSQUIRRELVM v) { SQ_FUNC_VARS(v); SQ_GET_STRING(v, 2, dbname); SQ_OPT_INTEGER(v, 3, flags, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_SUBLATIN_NA_LIKE); sqlite3 *db; int rc = sqlite3_open_v2(dbname, &db, flags, 0); if(rc != SQLITE_OK) return sq_throwerror(v, "Failed to open database ! %d", rc); sq_sqlite3_sdb *sdb = (sq_sqlite3_sdb *)sq_malloc(sizeof(sq_sqlite3_sdb)); memset(sdb, 0, sizeof(sq_sqlite3_sdb)); sdb->db = db; sdb->v = v; sq_resetobject(&sdb->busy_cb); sq_resetobject(&sdb->busy_udata); sq_resetobject(&sdb->progress_cb); sq_resetobject(&sdb->progress_udata); sq_resetobject(&sdb->trace_cb); sq_resetobject(&sdb->trace_udata); sq_resetobject(&sdb->null_value); sq_setinstanceup(v, 1, sdb); sq_setreleasehook(v,1, sq_sqlite3_releasehook); //save a weakref to allow statement return it's db sq_pushuserpointer(v, sdb->db); sq_weakref(v, 1); sq_setonregistrytable(v); return 1; } static SQRESULT sq_sqlite3_close(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); if(sq_sqlite3_close_release(v, sdb) == SQ_OK){ sq_setinstanceup(v, 1, 0); //next calls will fail with "database is closed" } else { return sq_throwerror(v, sqlite3_errmsg(self)); } return 0; } static SQRESULT sq_sqlite3_db_ptr(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); sq_pushuserpointer(v, self); return 1; } /* bool IsAutoCommitOn( ) */ static SQRESULT sq_sqlite3_IsAutoCommitOn(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); sq_pushbool(v, sqlite3_get_autocommit(self) ? SQTrue : SQFalse); return 1; } /* int changes( ) */ static SQRESULT sq_sqlite3_changes(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); sq_pushinteger(v, sqlite3_total_changes(self)); return 1; } static SQRESULT sq_sqlite3_exec(HSQUIRRELVM v){ return sqlite3_exec_fmt(v, tr_all_rows); } /* int exec_dml( const char * szSQL ) */ static SQRESULT sq_sqlite3_exec_dml(HSQUIRRELVM v){ return sqlite3_exec_fmt(v, tr_ddml); } /* bool exec_get_one( std::string & result , const char * szSQL ) */ static SQRESULT sq_sqlite3_exec_get_one(HSQUIRRELVM v){ return sqlite3_exec_fmt(v, tr_first_row_first_col); } static SQRESULT sq_sqlite3_exec_get_first_row(HSQUIRRELVM v){ return sqlite3_exec_fmt(v, tr_first_row); } /* const char * get_db_name( ) */ static SQRESULT sq_sqlite3_get_db_name(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); sq_pushstring(v, sqlite3_db_filename(self, "main"), -1); return 1; } /* sqlite3_int64 last_row_id( ) */ static SQRESULT sq_sqlite3_last_row_id(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); sq_pushinteger(v, sqlite3_last_insert_rowid(self)); return 1; } /* stmt * prepare( const char * sql ) */ static SQRESULT sq_sqlite3_prepare(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); SQ_GET_STRING(v, 2, sql); sq_pushstring(v, SQLite3_Stmt_TAG, -1); if(sq_getonroottable(v) == SQ_ERROR) return SQ_ERROR; sq_pushroottable(v); sq_push(v, 1); sq_push(v, 2); if(sq_call(v, 3, SQTrue, SQFalse) != SQ_OK) return SQ_ERROR; return 1; } /* void set_busy_timeout( int nMillisecs ) */ static SQRESULT sq_sqlite3_set_busy_timeout(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); SQ_GET_INTEGER(v, 2, nMillisecs); //self->set_busy_timeout(nMillisecs); return 0; } /* int total_changes( ) */ static SQRESULT sq_sqlite3_total_changes(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); sq_pushinteger(v, sqlite3_total_changes(self)); return 1; } static SQRESULT sq_sqlite3_enable_shared_cache(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); SQ_GET_BOOL(v, 2, bhow); sq_pushbool(v, sqlite3_enable_shared_cache(bhow) == SQLITE_OK); return 1; } static SQRESULT sq_sqlite3_sleep(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); SQ_GET_INTEGER(v, 2, n); sqlite3_sleep(n); return 0; } static SQRESULT sq_sqlite3_version(HSQUIRRELVM v) { sq_pushstring(v, sqlite3_libversion(), -1); return 1; } static SQRESULT sq_sqlite3_errcode(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); sq_pushinteger(v, sqlite3_errcode(self)); return 1; } static SQRESULT sq_sqlite3_errmsg(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); sq_pushstring(v, sqlite3_errmsg(self), -1); return 1; } #ifndef WIN32 static SQRESULT sq_sqlite3_temp_directory(HSQUIRRELVM v) { SQ_FUNC_VARS(v); const char *oldtemp = sqlite3_temp_directory; if (_top_ > 1) { SQ_GET_STRING(v, 2, temp); if (sqlite3_temp_directory) { sqlite3_free((char*)sqlite3_temp_directory); } if (temp && temp_size) { sqlite3_temp_directory = sqlite3_mprintf("%s", temp); } else { sqlite3_temp_directory = NULL; } } sq_pushstring(v, oldtemp, -1); return 1; } #endif #ifdef SQLITE_HAS_CODEC //#include "codecext.h" /* ** Params: db, sql ** returns: code, compiled length or error message */ static SQRESULT sq_sqlite3_key(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); SQ_GET_STRING(v, 2, key); sq_pushinteger(v, sqlite3_key(self, key, key_size)); return 1; } /* ** Params: db, sql ** returns: code, compiled length or error message */ static SQRESULT sq_sqlite3_rekey(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); SQ_GET_STRING(v, 2, key); sq_pushinteger(v, sqlite3_rekey(self, key, key_size)); return 1; } #endif #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK /* ** progress handler: ** Params: database, number of opcodes, callback function, userdata ** ** callback function: ** Params: userdata ** returns: 0 to return immediatly and return SQLITE_ABORT, non-zero to continue */ static int db_progress_callback(void *user) { int result = 1; /* abort by default */ sq_sqlite3_sdb *sdb = (sq_sqlite3_sdb*)user; HSQUIRRELVM v = sdb->v; int top = sq_gettop(v); sq_pushobject(v, sdb->progress_cb); sq_pushroottable(v); //this sq_pushobject(v, sdb->progress_udata); /* call lua function */ if (sq_call(v, 2, SQTrue, SQFalse) == SQ_OK) sq_getinteger(v, -1, &result); sq_settop(v, top); return result; } static SQRESULT sq_sqlite3_progress_handler(HSQUIRRELVM v) { SQ_FUNC_VARS(v); GET_sqlite3_INSTANCE(); if (_top_ < 2 || sq_gettype(v, 2) == OT_NULL) { sq_release(v, &sdb->progress_cb); sq_release(v, &sdb->progress_udata); sq_resetobject(&sdb->progress_cb); sq_resetobject(&sdb->progress_udata); /* clear busy handler */ sqlite3_progress_handler(self, 0, NULL, NULL); } else { SQ_GET_INTEGER(v, 2, nop); if(sq_gettype(v, 3) != OT_CLOSURE) return sq_throwerror(v, _SC("invalid second parameter expected closure")); sq_getstackobj(v, 3, &sdb->progress_cb); sq_addref(v, &sdb->progress_cb); if(_top_ > 3){ sq_getstackobj(v, 4, &sdb->progress_udata); sq_addref(v, &sdb->progress_udata); } /* set progress callback */ sqlite3_progress_handler(self, nop, db_progress_callback, sdb); } return 0; } #else /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */ static SQRESULT sq_sqlite3_progress_handler(HSQUIRRELVM v) { return sq_throwerror(v, _SC("progress callback support disabled at compile time")); return 0; } #endif /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */ /* ** trace callback: ** Params: database, callback function, userdata ** ** callback function: ** Params: userdata, sql */ static void db_trace_callback(void *user, const char *sql) { sq_sqlite3_sdb *sdb = (sq_sqlite3_sdb*)user; HSQUIRRELVM v = sdb->v; int top = sq_gettop(v); /* setup squirrel callback call */ sq_pushobject(v, sdb->trace_cb); sq_pushroottable(v); sq_pushobject(v, sdb->trace_udata); sq_pushstring(v, sql, -1); /* traced sql statement */ /* call squirrel function */ sq_call(v, 3, SQFalse, SQFalse); /* ignore any error generated by this function */ sq_settop(v, top); } static SQRESULT sq_sqlite3_trace(HSQUIRRELVM v) { SQ_FUNC_VARS(v); GET_sqlite3_INSTANCE(); if (_top_ < 2 || sq_gettype(v, 2) == OT_NULL) { sq_release(v, &sdb->trace_cb); sq_release(v, &sdb->trace_udata); sq_resetobject(&sdb->trace_cb); sq_resetobject(&sdb->trace_udata); /* clear trace handler */ sqlite3_trace(self, NULL, NULL); } else { if(sq_gettype(v, 2) != OT_CLOSURE) return sq_throwerror(v, _SC("invalid fisrt parameter expected closure")); sq_getstackobj(v, 2, &sdb->trace_cb); sq_addref(v, &sdb->trace_cb); if(_top_ > 2){ sq_getstackobj(v, 3, &sdb->trace_udata); sq_addref(v, &sdb->trace_udata); } /* set trace callback */ sqlite3_trace(self, db_trace_callback, sdb); } return 0; } /* ** busy handler: ** Params: database, callback function, userdata ** ** callback function: ** Params: userdata, number of tries ** returns: 0 to return immediatly and return SQLITE_BUSY, non-zero to try again */ static int db_busy_callback(void *user, int tries) { SQBool retry = SQFalse; /* abort by default */ sq_sqlite3_sdb *sdb = (sq_sqlite3_sdb*)user; HSQUIRRELVM v = sdb->v; int top = sq_gettop(v); sq_pushobject(v, sdb->busy_cb); sq_pushroottable(v); sq_pushobject(v, sdb->busy_udata); sq_pushinteger(v, tries); /* call lua function */ if (sq_call(v, 3, SQTrue, SQFalse) == SQ_OK) sq_getbool(v, -1, &retry); sq_settop(v, top); return retry == SQTrue; } static SQRESULT sq_sqlite3_busy_handler(HSQUIRRELVM v) { SQ_FUNC_VARS(v); GET_sqlite3_INSTANCE(); if (_top_ < 2 || sq_gettype(v, 2) == OT_NULL) { sq_release(v, &sdb->busy_cb); sq_release(v, &sdb->busy_udata); sq_resetobject(&sdb->busy_cb); sq_resetobject(&sdb->busy_udata); /* clear busy handler */ sqlite3_busy_handler(self, NULL, NULL); } else { if(sq_gettype(v, 2) != OT_CLOSURE) return sq_throwerror(v, _SC("invalid fisrt parameter expected closure")); sq_getstackobj(v, 2, &sdb->busy_cb); sq_addref(v, &sdb->busy_cb); if(_top_ > 2){ sq_getstackobj(v, 3, &sdb->busy_udata); sq_addref(v, &sdb->busy_udata); } /* set busy callback */ sqlite3_busy_handler(self, db_busy_callback, sdb); } return 0; } static SQRESULT sq_sqlite3_busy_timeout(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); SQ_GET_INTEGER(v, 2, timeout); sqlite3_busy_timeout(self, timeout); /* if there was a timeout callback registered, it is now ** invalid/useless. free any references we may have */ sq_release(v, &sdb->busy_cb); sq_resetobject(&sdb->busy_cb); sq_release(v, &sdb->busy_udata); sq_resetobject(&sdb->busy_udata); return 0; } static SQRESULT sq_sqlite3_interrupt(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_INSTANCE(); sqlite3_interrupt(self); return 0; } /* ** ======================================================= ** User Defined Functions - Context Methods ** ======================================================= */ typedef struct { sqlite3_context *ctx; HSQOBJECT udata; } sq_sqlite3_context_st; static const SQChar sq_sqlite3_context_TAG[] = _SC(":sqlite3:ctx"); static SQRESULT sq_sqlite3_context_releasehook(SQUserPointer p, SQInteger size, HSQUIRRELVM v) { sq_sqlite3_context_st *ctx = ((sq_sqlite3_context_st *)p); /* 'free' all references */ sq_free(ctx, sizeof(sq_sqlite3_context_st)); return 0; } static SQRESULT sq_sqlite3_context_constructor(HSQUIRRELVM v) { sq_sqlite3_context_st *ctx = (sq_sqlite3_context_st*)sq_malloc(sizeof(sq_sqlite3_context_st)); ctx->ctx = NULL; sq_resetobject(&ctx->udata); sq_setinstanceup(v, 1, ctx); sq_setreleasehook(v,1, sq_sqlite3_context_releasehook); return 1; } #define SQ_SQLITE3_GETCONTEXT(v, idx) \ sq_sqlite3_context_st *self;\ if((_rc_ = sq_getinstanceup(v, idx, (void**)&self, (void*)sq_sqlite3_context_TAG)) < 0) return _rc_; #define GET_sqlite3_context_INSTANCE() SQ_SQLITE3_GETCONTEXT(v,1) static SQRESULT sq_sqlite3_context__tostring(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_context_INSTANCE(); char buff[64]; snprintf(buff, sizeof(buff), "sqlite function context (%p)", self); sq_pushstring(v, buff, -1); return 1; } static SQRESULT sq_sqlite3_context_check_aggregate(HSQUIRRELVM v, sq_sqlite3_context_st *ctx) { sq_sqlite3_sdb_func *func = (sq_sqlite3_sdb_func*)sqlite3_user_data(ctx->ctx); if (sq_isclosure(func->fn_finalize)) { return sq_throwerror(v, "attempt to call aggregate method from scalar function"); } return 1; } static SQRESULT sq_sqlite3_context_user_data(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_context_INSTANCE(); sq_sqlite3_sdb_func *func = (sq_sqlite3_sdb_func*)sqlite3_user_data(self->ctx); sq_pushobject(v, func->udata); return 1; } static SQRESULT sq_sqlite3_context_aggregate_data(HSQUIRRELVM v) { SQ_FUNC_VARS(v); GET_sqlite3_context_INSTANCE(); sq_sqlite3_context_check_aggregate(v, self); if(_top_ < 2){ sq_pushobject(v, self->udata); } else { sq_release(v, &self->udata); sq_getstackobj(v, 2, &self->udata); sq_addref(v, &self->udata); } return 0; } static SQRESULT sq_sqlite3_context_aggregate_count(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_context_INSTANCE(); sq_sqlite3_context_check_aggregate(v, self); sq_pushinteger(v, sqlite3_aggregate_count(self->ctx)); return 1; } #if 0 void *sqlite3_get_auxdata(sqlite3_context*, int); void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*)); #endif static SQRESULT sq_sqlite3_context_result_blob(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_context_INSTANCE(); SQ_GET_STRING(v, 2, blob); sqlite3_result_blob(self->ctx, (const void*)blob, blob_size, SQLITE_TRANSIENT); return 0; } static SQRESULT sq_sqlite3_context_result_double(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_context_INSTANCE(); SQ_GET_FLOAT(v, 2, d); sqlite3_result_double(self->ctx, d); return 0; } static SQRESULT sq_sqlite3_context_result_error(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_context_INSTANCE(); SQ_GET_STRING(v,2, err); sqlite3_result_error(self->ctx, err, err_size); return 0; } static SQRESULT sq_sqlite3_context_result_int(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_context_INSTANCE(); SQ_GET_INTEGER(v, 2, i); sqlite3_result_int(self->ctx, i); return 0; } static SQRESULT sq_sqlite3_context_result_null(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_context_INSTANCE(); sqlite3_result_null(self->ctx); return 0; } static SQRESULT sq_sqlite3_context_result_text(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_sqlite3_context_INSTANCE(); SQ_GET_STRING(v, 2, text); sqlite3_result_text(self->ctx, text, text_size, SQLITE_TRANSIENT); return 0; } #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_sqlite3_context_##name,nparams,tycheck} static SQRegFunction sq_sqlite3_context_methods[] = { _DECL_FUNC(constructor, 1, _SC("x")), _DECL_FUNC(user_data, 1, _SC("x")), _DECL_FUNC(aggregate_data, -1, _SC("x.")), _DECL_FUNC(aggregate_count, 1, _SC("x")), _DECL_FUNC(result_null, 1, _SC("x")), _DECL_FUNC(result_double, 2, _SC("xf")), _DECL_FUNC(result_int, 2, _SC("xi")), _DECL_FUNC(result_text, 2, _SC("xs")), _DECL_FUNC(result_blob, 2, _SC("xs")), _DECL_FUNC(result_error, 2, _SC("xs")), _DECL_FUNC(_tostring, -1, _SC("x")), {0,0} }; #undef _DECL_FUNC /* ** callback functions used when calling registered sql functions */ static void sqlite3_push_value(HSQUIRRELVM v, sqlite3_value *value) { const char *tmp_value; switch (sqlite3_value_type(value)) { case SQLITE_TEXT: tmp_value = (const char*) sqlite3_value_text(value); if(tmp_value) sq_pushstring(v, tmp_value, sqlite3_value_bytes(value)); else push_sqlite3_null(v); break; case SQLITE_INTEGER: { sqlite_int64 i64 = sqlite3_value_int64(value); SQInteger n = (SQInteger)i64; if (n == i64) sq_pushinteger(v, n); else sq_pushstring(v, (const char*) sqlite3_value_text(value), sqlite3_value_bytes(value)); } break; case SQLITE_FLOAT: sq_pushfloat(v, sqlite3_value_double(value)); break; case SQLITE_BLOB: tmp_value = (const char*) sqlite3_value_blob(value); if(tmp_value) sq_pushstring(v, tmp_value, sqlite3_value_bytes(value)); else push_sqlite3_null(v); break; case SQLITE_NULL: push_sqlite3_null(v); break; default: /* things done properly (SQLite + Lua SQLite) ** this should never happen */ push_sqlite3_null(v); break; } } //#if 0 static SQRESULT new_context_instance(HSQUIRRELVM v, sq_sqlite3_context_st **ctx){ sq_pushregistrytable(v); sq_pushstring(v,_SC("sqite3_context"),-1); int rc = sq_rawget(v, -2); sq_remove(v, -2); //remove registrytable sq_pushroottable(v); rc = sq_call(v, 1, SQTrue, SQFalse); sq_remove(v, -2); //class rc = sq_getinstanceup(v, -1, (void**)ctx, (void*)sq_sqlite3_context_TAG); return rc; } /* scalar function to be called ** callback params: context, values... */ static void db_sql_normal_function(sqlite3_context *context, int argc, sqlite3_value **argv) { sq_sqlite3_sdb_func *func = (sq_sqlite3_sdb_func*)sqlite3_user_data(context); HSQUIRRELVM v = func->sdb->v; int n; sq_sqlite3_context_st *ctx; int top = sq_gettop(v); /* ensure there is enough space in the stack */ sq_reservestack(v, argc + 5); sq_pushobject(v, func->fn_step); sq_pushroottable(v); if (!sq_isclosure(func->fn_finalize)) { new_context_instance(v, &ctx); } else { /* reuse context userdata value */ void *p = sqlite3_aggregate_context(context, 1); /* i think it is OK to use assume that using a light user data ** as an entry on LUA REGISTRY table will be unique */ sq_pushregistrytable(v); sq_pushuserpointer(v, p); /* context table */ if(sq_rawget(v, -2) != SQ_OK){ /* not yet created? */ sq_poptop(v); //remove null new_context_instance(v, &ctx); sq_pushuserpointer(v, p); sq_push(v, -2); sq_rawset(v, -4); //insert into registrytable } sq_remove(v, -2); //remove registrytable } /* push params */ for (n = 0; n < argc; ++n) { sqlite3_push_value(v, argv[n]); } /* set context */ ctx->ctx = context; if (sq_call(v, argc + 2, SQFalse, SQFalse) != SQ_OK) { //2 = roottable + ctx sqlite3_result_error(context, sq_getlasterror_str(v), -1); } /* invalidate context */ ctx->ctx = NULL; if (!sq_isclosure(func->fn_finalize)) { sq_release(v, &ctx->udata); sq_resetobject(&ctx->udata); } sq_settop(v, top); } static void db_sql_finalize_function(sqlite3_context *context) { sq_sqlite3_sdb_func *func = (sq_sqlite3_sdb_func*)sqlite3_user_data(context); HSQUIRRELVM v = func->sdb->v; void *p = sqlite3_aggregate_context(context, 1); /* minimal mem usage */ sq_sqlite3_context_st *ctx; int top = sq_gettop(v); sq_pushobject(v, func->fn_finalize); sq_pushroottable(v); /* i think it is OK to use assume that using a light user data ** as an entry on LUA REGISTRY table will be unique */ sq_pushregistrytable(v); sq_pushuserpointer(v, p); /* remove it from registry but we'll use it last time here */ /* context table */ if(sq_deleteslot(v, -2, SQTrue) != SQ_OK){ /* not yet created? - shouldn't happen in finalize function */ sq_pop(v, 1); new_context_instance(v, &ctx); sq_pushuserpointer(v, p); sq_push(v, -2); sq_rawset(v, -4); } sq_remove(v, -2); //registrytable /* set context */ ctx->ctx = context; if (sq_call(v, 1, SQFalse, SQFalse) != SQ_OK) { sqlite3_result_error(context, sq_getlasterror_str(v), -1); } /* invalidate context */ ctx->ctx = NULL; /* cleanup context */ sq_release(v, &ctx->udata); sq_resetobject(&ctx->udata); sq_settop(v, top); } /* ** Register a normal function ** Params: db, function name, number arguments, [ callback | step, finalize], user data ** Returns: true on sucess ** ** Normal function: ** Params: context, params ** ** Aggregate function: ** Params of step: context, params ** Params of finalize: context */ static SQRESULT db_register_function(HSQUIRRELVM v, int aggregate) { SQ_FUNC_VARS(v); GET_sqlite3_INSTANCE(); SQ_GET_STRING(v, 2, name); SQ_GET_INTEGER(v, 3, nargs); if(sq_gettype(v, 4) != OT_CLOSURE) return sq_throwerror(v, "invalid parameter 3 expected closure"); if (aggregate) { if(sq_gettype(v,5) != OT_CLOSURE) return sq_throwerror(v, "invalid parameter 4 expected closure"); } sq_sqlite3_sdb_func *func; /* maybe an alternative way to allocate memory should be used/avoided */ func = (sq_sqlite3_sdb_func*)sq_malloc(sizeof(sq_sqlite3_sdb_func)); memset(func, 0, sizeof(sq_sqlite3_sdb_func)); _rc_ = sqlite3_create_function( self, name, nargs, SQLITE_UTF8, func, aggregate ? NULL : db_sql_normal_function, aggregate ? db_sql_normal_function : NULL, aggregate ? db_sql_finalize_function : NULL ); if (_rc_ == SQLITE_OK) { /* save registered function in db function list */ func->sdb = sdb; func->next = sdb->func; sdb->func = func; /* save the setp/normal function callback */ sq_resetobject(&func->fn_step); sq_getstackobj(v, 4, &func->fn_step); sq_addref(v, &func->fn_step); /* save the finalize function callback */ sq_resetobject(&func->fn_finalize); if (aggregate) { sq_getstackobj(v, 5, &func->fn_finalize); sq_addref(v, &func->fn_finalize); } /* save user data */ sq_resetobject(&func->udata); int udata_idx = aggregate ? 6 : 5; if(_top_ >= udata_idx){ sq_getstackobj(v, udata_idx, &func->udata); sq_addref(v, &func->udata); } } else { /* free allocated memory */ sq_free(func, sizeof(sq_sqlite3_sdb_func)); } sq_pushbool(v, _rc_ == SQLITE_OK ? 1 : 0); return 1; } //#endif static SQRESULT sq_sqlite3_create_function(HSQUIRRELVM v) { return db_register_function(v, 0); } static SQRESULT sq_sqlite3_create_aggregate(HSQUIRRELVM v) { return db_register_function(v, 1); } #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_sqlite3_##name,nparams,tycheck} static SQRegFunction sq_sqlite3_methods[] = { _DECL_FUNC(constructor, -2, _SC("xsi")), _DECL_FUNC(close, 1, _SC("x")), _DECL_FUNC(db_ptr, 1, _SC("x")), _DECL_FUNC(IsAutoCommitOn, 1, _SC("x")), _DECL_FUNC(close, 1, _SC("x")), _DECL_FUNC(version, 1, _SC("x")), _DECL_FUNC(errcode, 1, _SC("x")), _DECL_FUNC(errmsg, 1, _SC("x")), _DECL_FUNC(sleep, 1, _SC("x")), _DECL_FUNC(interrupt, 1, _SC("x")), _DECL_FUNC(progress_handler, -2, _SC("x i|o c .")), _DECL_FUNC(trace, -2, _SC("x c|o .")), _DECL_FUNC(busy_handler, -2, _SC("x c|o .")), _DECL_FUNC(busy_timeout, 2, _SC("xi")), _DECL_FUNC(create_function, 4, _SC("xsic")), _DECL_FUNC(create_aggregate, 5, _SC("xsicc")), #ifndef WIN32 _DECL_FUNC(temp_directory, -2, _SC("xs")), #endif _DECL_FUNC(enable_shared_cache, 2, _SC("xb")), _DECL_FUNC(changes, 1, _SC("x")), _DECL_FUNC(exec, -2, _SC("xs")), _DECL_FUNC(exec_dml, -2, _SC("xs")), _DECL_FUNC(exec_get_first_row, -2, _SC("xs")), _DECL_FUNC(exec_get_one, -2, _SC("xs")), _DECL_FUNC(get_db_name, 1, _SC("x")), _DECL_FUNC(last_row_id, 1, _SC("x")), _DECL_FUNC(prepare, -2, _SC("xs")), _DECL_FUNC(set_busy_timeout, -1, _SC("xi")), _DECL_FUNC(total_changes, 1, _SC("x")), #ifdef SQLITE_HAS_CODEC _DECL_FUNC(key, 2, _SC("xs")), _DECL_FUNC(rekey, 2, _SC("xs")), #endif {0,0} }; #undef _DECL_FUNC #define INT_CONST(v,num) sq_pushstring(v,_SC(#num),-1);sq_pushinteger(v,num);sq_newslot(v,-3,SQTrue); #ifdef __cplusplus extern "C" { #endif SQRESULT sqext_register_SQLite3(HSQUIRRELVM v) { sq_insertfunc(v, _SC("sle2vecOfvec"), sq_sle2vecOfvec, -2, _SC(". s|x a i"), SQTrue); sq_insertfunc(v, _SC("get_sle_size"), sq_get_sle_size, -2, _SC(".i"), SQTrue); HSQOBJECT sqlite3_NULL; sq_pushregistrytable(v); sq_pushstring(v, sqlite3_NULL_Name,-1); sq_newuserdata(v, sizeof(void*)); sq_resetobject(&sqlite3_NULL); sq_getstackobj(v, -1, &sqlite3_NULL); sq_newtable(v); sq_insert_reg_funcs(v, sqlite3_NULL_methods); sq_setdelegate(v, -2); sq_newslot(v,-3,SQTrue); sq_pushstring(v,_SC("sqite3_context"),-1); sq_newclass(v,SQFalse); sq_settypetag(v,-1,(void*)sq_sqlite3_context_TAG); sq_insert_reg_funcs(v, sq_sqlite3_context_methods); sq_newslot(v,-3,SQTrue); sq_poptop(v); //remove registrytable sq_pushstring(v,SQLite3_TAG,-1); sq_newclass(v,SQFalse); sq_settypetag(v,-1,(void*)SQLite3_TAG); sq_insert_reg_funcs(v, sq_sqlite3_methods); INT_CONST(v,SQLITE_OPEN_CREATE); INT_CONST(v,SQLITE_OPEN_READWRITE); INT_CONST(v,SQLITE_OPEN_SHAREDCACHE); INT_CONST(v,SQLITE_OPEN_SUBLATIN_NA_LIKE); INT_CONST(v,SQLITE_OK); INT_CONST(v,SQLITE_INTERRUPT); //push sqlite3_NULL as a member sq_pushstring(v, nullName,-1); sq_pushobject(v, sqlite3_NULL); sq_newslot(v,-3,SQTrue); sq_newslot(v,-3,SQTrue); sq_pushstring(v, SQLite3_Stmt_TAG,-1); sq_newclass(v,SQFalse); sq_settypetag(v,-1,(void*)SQLite3_Stmt_TAG); sq_insert_reg_funcs(v, sq_sqlite3_stmt_methods); INT_CONST(v,SQLITE_OK); INT_CONST(v,SQLITE_ROW); INT_CONST(v,SQLITE_DONE); INT_CONST(v,AS_STRING_ALWAYS); INT_CONST(v,NULL_AS_EMPTY_STR); //push sqlite3_NULL as a member sq_pushstring(v, nullName,-1); sq_pushobject(v, sqlite3_NULL); sq_newslot(v,-3,SQTrue); sq_newslot(v,-3,SQTrue); return 1; } #ifdef __cplusplus } #endif