Explorar o código

Update sqlite3 and split the sq_sqlite3 extension into header and source.

mingodad %!s(int64=10) %!d(string=hai) anos
pai
achega
c63177e24f
Modificáronse 2 ficheiros con 403 adicións e 119 borrados
  1. 344 119
      SquiLu-ext/sq_sqlite3.cpp
  2. 59 0
      SquiLu-ext/sq_sqlite3.h

+ 344 - 119
SquiLu-ext/sq_sqlite3.cpp

@@ -1,55 +1,24 @@
 /* Code generated by script */
-#include "squirrel.h"
-#include "sqlite3.h"
-#include <string.h>
-#include <stdio.h>
-#include "sqstdblobimpl.h"
 
-typedef struct sq_sqlite3_sdb sq_sqlite3_sdb;
-typedef struct sq_sqlite3_sdb_func sq_sqlite3_sdb_func;
+#include "sq_sqlite3.h"
 
-/* to use as C user data so i know what function sqlite is calling */
-struct sq_sqlite3_sdb_func
+//return error if parameter exists but is not a blob
+//return 1 if the parameter exists and is a blob
+//return 0 if the parameter doesn't exists and creates a new blob
+static SQRESULT getOptionalBlob(HSQUIRRELVM v, SQInteger idx, SQBlob **blob)
 {
-    /* 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";
+    if(sq_gettop(v) >= idx) {
+        if(SQ_FAILED(sq_getinstanceup(v,3,(SQUserPointer*)blob,(SQUserPointer)SQBlob::SQBlob_TAG)))
+            return sq_throwerror(v,_SC("expect a blob as second parameter"));
+        if(!(*blob) || !(*blob)->IsValid())
+            return sq_throwerror(v,_SC("the blob is invalid"));
+    } else {
+        *blob = new SQBlob(0, 8192);
+        return 0;
+    }
+    return 1;
+}
 
-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);
@@ -102,22 +71,43 @@ 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
 #define WITH_COL_NAMES 0x04
+#define WITH_BLOB_AS_NULL 0x08
+#define WITH_SQ_NULL 0x100
 
 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);
+    else
+    {
+        if(flags & AS_STRING_ALWAYS) sq_pushliteral(v, "");
+        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);
+    int col_type = 0;
+    if(flags & WITH_BLOB_AS_NULL)
+    {
+        col_type = sqlite3_column_type(stmt, col);
+        if(col_type == SQLITE_BLOB)
+        {
+            if(flags & AS_STRING_ALWAYS) sq_pushliteral(v, "");
+            else push_sqlite3_null(v);
+            return 1;
+        }
+    }
+    if(flags & AS_STRING_ALWAYS)
+    {
+        sqlite3_stmt_push_string(v, stmt, col, flags);
+    }
     else
     {
-        switch (sqlite3_column_type(stmt, col))
+        if(!col_type) col_type = sqlite3_column_type(stmt, col);
+
+        switch (col_type)
         {
         case SQLITE_INTEGER:
         {
@@ -141,10 +131,12 @@ static SQRESULT sqlite3_stmt_push_value(HSQUIRRELVM v, sqlite3_stmt *stmt, int c
             else push_sqlite3_null(v);
             break;
         case SQLITE_NULL:
-            push_sqlite3_null(v);
+            if(flags & WITH_SQ_NULL) sq_pushnull(v);
+            else push_sqlite3_null(v);
             break;
         default:
-            push_sqlite3_null(v);
+            if(flags & WITH_SQ_NULL) sq_pushnull(v);
+            else push_sqlite3_null(v);
             break;
         }
     }
@@ -495,6 +487,57 @@ static SQRESULT sq_sqlite3_stmt_col_count(HSQUIRRELVM v)
     return 1;
 }
 
+static SQRESULT sq_sqlite3_stmt_col_name(HSQUIRRELVM v)
+{
+    SQ_FUNC_VARS_NO_TOP(v);
+    GET_sqlite3_stmt_INSTANCE();
+    SQ_GET_INTEGER(v, 2, col);
+    sq_pushstring(v, sqlite3_column_name(self, col), -1);
+    return 1;
+}
+
+static SQRESULT sq_sqlite3_stmt_col_type(HSQUIRRELVM v)
+{
+    SQ_FUNC_VARS_NO_TOP(v);
+    GET_sqlite3_stmt_INSTANCE();
+    SQ_GET_INTEGER(v, 2, col);
+    sq_pushinteger(v, sqlite3_column_type(self, col));
+    return 1;
+}
+
+static const char *get_column_type(sqlite3_stmt *stmt, int col)
+{
+    const char *column_type;
+    int icol_type = sqlite3_column_type(stmt, col);
+    switch(icol_type)
+    {
+        case SQLITE_INTEGER: column_type = "INTEGER"; break;
+        case SQLITE_FLOAT: column_type = "FLOAT"; break;
+        case SQLITE_BLOB: column_type = "BLOB"; break;
+        case SQLITE_TEXT: column_type = "TEXT"; break;
+        case SQLITE_NULL: column_type = "NULL"; break;
+        default:
+            column_type = "unknown";
+    }
+    return column_type;
+}
+
+static const char *get_column_dcltype(sqlite3_stmt *stmt, int col)
+{
+    const char *column_type = sqlite3_column_decltype(stmt, col);
+    if(!column_type) column_type = get_column_type(stmt, col);
+    return column_type;
+}
+
+static SQRESULT sq_sqlite3_stmt_col_declared_type(HSQUIRRELVM v)
+{
+    SQ_FUNC_VARS_NO_TOP(v);
+    GET_sqlite3_stmt_INSTANCE();
+    SQ_GET_INTEGER(v, 2, col);
+    sq_pushstring(v, get_column_dcltype(self, col), -1);
+    return 1;
+}
+
 static SQRESULT sq_sqlite3_stmt_colsAsArray(HSQUIRRELVM v)
 {
     SQ_FUNC_VARS_NO_TOP(v);
@@ -509,45 +552,60 @@ static SQRESULT sq_sqlite3_stmt_colsAsArray(HSQUIRRELVM v)
     return 1;
 }
 
-static SQRESULT sq_sqlite3_stmt_colsAsTable(HSQUIRRELVM v)
+static SQRESULT sq_sqlite3_stmt_colsTypeAsArray(HSQUIRRELVM v)
 {
     SQ_FUNC_VARS_NO_TOP(v);
     GET_sqlite3_stmt_INSTANCE();
     int col_count = sqlite3_column_count(self);
-    sq_newtable(v);
+    sq_newarray(v, col_count);
     for(int i=0; i<col_count; ++i)
     {
-        sq_pushstring(v, sqlite3_column_name(self, i), -1);
-        sq_pushinteger(v, i);
-        sq_rawset(v, -3);
+        sq_pushstring(v, get_column_type(self, i), -1);
+        sq_arrayset(v, -2, i);
     }
     return 1;
 }
 
-static SQRESULT sq_sqlite3_stmt_col_name(HSQUIRRELVM v)
+static SQRESULT sq_sqlite3_stmt_colsDeclTypeAsArray(HSQUIRRELVM v)
 {
     SQ_FUNC_VARS_NO_TOP(v);
     GET_sqlite3_stmt_INSTANCE();
-    SQ_GET_INTEGER(v, 2, col);
-    sq_pushstring(v, sqlite3_column_name(self, col), -1);
+    int col_count = sqlite3_column_count(self);
+    sq_newarray(v, col_count);
+    for(int i=0; i<col_count; ++i)
+    {
+        sq_pushstring(v, get_column_dcltype(self, i), -1);
+        sq_arrayset(v, -2, i);
+    }
     return 1;
 }
 
-static SQRESULT sq_sqlite3_stmt_col_type(HSQUIRRELVM v)
+static SQRESULT sq_sqlite3_stmt_colsNameAsArray(HSQUIRRELVM v)
 {
     SQ_FUNC_VARS_NO_TOP(v);
     GET_sqlite3_stmt_INSTANCE();
-    SQ_GET_INTEGER(v, 2, col);
-    sq_pushinteger(v, sqlite3_column_type(self, col));
+    int col_count = sqlite3_column_count(self);
+    sq_newarray(v, col_count);
+    for(int i=0; i<col_count; ++i)
+    {
+        sq_pushstring(v, sqlite3_column_name(self, i), -1);
+        sq_arrayset(v, -2, i);
+    }
     return 1;
 }
 
-static SQRESULT sq_sqlite3_stmt_col_declared_type(HSQUIRRELVM v)
+static SQRESULT sq_sqlite3_stmt_colsAsTable(HSQUIRRELVM v)
 {
     SQ_FUNC_VARS_NO_TOP(v);
     GET_sqlite3_stmt_INSTANCE();
-    SQ_GET_INTEGER(v, 2, col);
-    sq_pushstring(v, sqlite3_column_decltype(self, col), -1);
+    int col_count = sqlite3_column_count(self);
+    sq_newtable(v);
+    for(int i=0; i<col_count; ++i)
+    {
+        sq_pushstring(v, sqlite3_column_name(self, i), -1);
+        sq_pushinteger(v, i);
+        sq_rawset(v, -3);
+    }
     return 1;
 }
 
@@ -703,6 +761,9 @@ static void append_escaping_json(SQBlob &json, const char *sz)
             case '"':
             case '\n':
             case '\r':
+            case '\t':
+            case '\b':
+            case '\f':
             case '\\':
             {
                 json.Write(sz+last_idx, idx-last_idx);
@@ -712,9 +773,24 @@ static void append_escaping_json(SQBlob &json, const char *sz)
                     json.Write("\\n", 2);
                     ++last_idx;
                 }
+                else if(c == '\t')
+                {
+                    json.Write("\\t", 2);
+                    ++last_idx;
+                }
+                else if(c == '\b')
+                {
+                    json.Write("\\b", 2);
+                    ++last_idx;
+                }
+                else if(c == '\f')
+                {
+                    json.Write("\\f", 2);
+                    ++last_idx;
+                }
                 else if(c == '\r')
                 {
-                    //skip it
+                    json.Write("\\r", 2);
                     ++last_idx;
                 }
                 else
@@ -735,64 +811,74 @@ static SQRESULT sq_sqlite3_stmt_asJsonArray(HSQUIRRELVM v)
     GET_sqlite3_stmt_INSTANCE();
     SQ_OPT_BOOL(v, 2, withMetadata, 0);
     int col_count = sqlite3_column_count(self);
-    int i;
+    int i, value_type;
     const char* value;
 
-    SQBlob json(0, 8192);
+    SQBlob *json = NULL;
+    int has_blob = getOptionalBlob(v, 3, &json);
+    if(has_blob == SQ_ERROR) return SQ_ERROR;
 
     if(withMetadata)
     {
-        json.WriteZstr("{\n\"columns\":[\n");
+        json->WriteZstr("{\n\"columns\":[\n");
         for(i=0; i < col_count; ++i)
         {
-            json.WriteZstr((i == 0 ? "\"" : ",\""));
-            json.WriteZstr(sqlite3_column_name(self, i));
-            json.WriteZstr("\"");
+            json->WriteZstr((i == 0 ? "\"" : ",\""));
+            const char *column_name = sqlite3_column_name(self, i);
+            json->WriteZstr(column_name ? column_name : "unknown");
+            json->WriteZstr("\"");
         }
 
-        json.WriteZstr("\n],\n\"column_types\":[\n");
+        json->WriteZstr("\n],\n\"column_types\":[\n");
         for(i=0; i < col_count; ++i)
         {
-            json.WriteZstr((i == 0 ? "\"" : ",\""));
-            json.WriteZstr(sqlite3_column_decltype(self, i));
-            json.WriteZstr("\"");
+            json->WriteZstr((i == 0 ? "\"" : ",\""));
+            json->WriteZstr(get_column_dcltype(self, i));
+            json->WriteZstr("\"");
         }
 
-        json.WriteZstr("\n],\n\"rows\":[\n");
+        json->WriteZstr("\n],\n\"rows\":[\n");
     }
     else
     {
-        json.WriteZstr("[\n");
+        json->WriteZstr("[\n");
     }
     int row_count = 0;
     while(sqlite3_step(self) == SQLITE_ROW)
     {
-        json.WriteZstr((row_count++ == 0 ? "[" : ",["));
+        json->WriteZstr((row_count++ == 0 ? "[" : ",["));
         for(i=0; i < col_count; ++i)
         {
-            json.WriteZstr((i == 0 ? "\"" : ",\""));
+            json->WriteZstr((i == 0 ? "\"" : ",\""));
+            //if get the type after sqlite3_column_text it always returns SQLITE_TEXT
+            value_type = sqlite3_column_type(self, i);
             value = (const char*)sqlite3_column_text(self, i);
             if(value)
             {
-                switch(sqlite3_column_type(self, i))
+                switch(value_type)
                 {
+                case SQLITE_BLOB:
+                    break; //no blobs in json
                 case SQLITE_TEXT:
                 {
-                    append_escaping_json(json, value);
+                    append_escaping_json(*json, value);
                 }
                 break;
                 default:
-                    json.WriteZstr(value);
+                    json->WriteZstr(value);
                 }
             }
-            json.WriteZstr("\"");
+            json->WriteZstr("\"");
         }
-        json.WriteZstr("]\n");
+        json->WriteZstr("]\n");
     }
-    json.WriteZstr("]");
-    if(withMetadata) json.WriteZstr("\n}");
+    json->WriteZstr("]");
+    if(withMetadata) json->WriteZstr("\n}");
+
+    if(has_blob) return 0;
+    sq_pushstring(v, (const SQChar*)json->GetBuf(), json->Len());
+    delete json;
 
-    sq_pushstring(v, (const SQChar*)json.GetBuf(), json.Len());
     return 1;
 }
 
@@ -801,39 +887,50 @@ static SQRESULT sq_sqlite3_stmt_asJsonObject(HSQUIRRELVM v)
     SQ_FUNC_VARS_NO_TOP(v);
     GET_sqlite3_stmt_INSTANCE();
     int col_count = sqlite3_column_count(self);
-    int i;
+    int i, value_type, record_count = 0;
     const char* value;
 
-    SQBlob json(0, 8192);
+    SQBlob *json = NULL;
+    int has_blob = getOptionalBlob(v, 2, &json);
+    if(has_blob == SQ_ERROR) return SQ_ERROR;
 
-    json.WriteZstr("{");
+    json->WriteZstr("{");
     if(sqlite3_step(self) == SQLITE_ROW)
     {
+        ++record_count;
         for(i=0; i < col_count; ++i)
         {
-            json.WriteZstr((i == 0 ? "\"" : "\",\""));
-            append_escaping_json(json, (const char*)sqlite3_column_name(self, i));
-            json.WriteZstr("\":\"");
+            json->WriteZstr((i == 0 ? "\"" : "\",\""));
+            append_escaping_json(*json, (const char*)sqlite3_column_name(self, i));
+            json->WriteZstr("\":\"");
+            //if get the type after sqlite3_column_text it always returns SQLITE_TEXT
+            value_type = sqlite3_column_type(self, i);
             value = (const char*)sqlite3_column_text(self, i);
             if(value)
             {
-                switch(sqlite3_column_type(self, i))
+                switch(value_type)
                 {
+                case SQLITE_BLOB:
+                    break; //no blobs in json
                 case SQLITE_TEXT:
                 {
-                    append_escaping_json(json, value);
+                    append_escaping_json(*json, value);
                 }
                 break;
                 default:
-                    json.WriteZstr(value);
+                    json->WriteZstr(value);
                 }
             }
         }
-        json.WriteZstr("\"");
+        json->WriteZstr("\"");
     }
-    json.WriteZstr("}");
+    json->WriteZstr("}");
+
+    if(has_blob) return 0;
+    if(record_count) sq_pushstring(v, (const SQChar*)json->GetBuf(), json->Len());
+    else sq_pushnull(v);
+    delete json;
 
-    sq_pushstring(v, (const SQChar*)json.GetBuf(), json.Len());
     return 1;
 }
 
@@ -1102,41 +1199,75 @@ static SQRESULT sq_sqlite3_stmt_asSleArray(HSQUIRRELVM v)
     int i;
     const char *value;
 
-    SQBlob sle(0, 8192);
+    SQBlob *sle = NULL;
+    int has_blob = getOptionalBlob(v, 2, &sle);
+    if(has_blob == SQ_ERROR) return SQ_ERROR;
 
-    sle.WriteZstr("[[");
+    sle->WriteZstr("[[");
     for(i=0; i < col_count; ++i)
     {
         value = sqlite3_column_name(self, i);
-        add2sle(sle, value, strlen(value));
+        add2sle(*sle, value, strlen(value));
     }
-    sle.WriteChar(SLEEND);
-    sle.WriteChar(']');
+    sle->WriteChar(SLEEND);
+    sle->WriteChar(']');
 
     while(sqlite3_step(self) == SQLITE_ROW)
     {
-        sle.WriteChar('[');
+        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),
+                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),
+                add2sle(*sle, (const char*)sqlite3_column_text(self, i),
                         sqlite3_column_bytes(self, i));
             }
         }
-        sle.WriteChar(SLEEND);
-        sle.WriteChar(']');
+        sle->WriteChar(SLEEND);
+        sle->WriteChar(']');
     }
-    sle.WriteChar(']');
+    sle->WriteChar(']');
+
+    if(has_blob) return 0;
+    sq_pushstring(v, (const SQChar*)sle->GetBuf(), sle->Len());
+    delete sle;
+    return 1;
+}
 
-    sq_pushstring(v, (const SQChar*)sle.GetBuf(), sle.Len());
+static SQRESULT sq_sqlite3_stmt_all_blobs_size(HSQUIRRELVM v)
+{
+    SQ_FUNC_VARS(v);
+    GET_sqlite3_stmt_INSTANCE();
+    int total_size = 0, col_count = sqlite3_column_count(self);
+    for(int i=0; i < col_count; ++i)
+    {
+        if(sqlite3_column_type(self, i) == SQLITE_BLOB)
+        {
+            total_size += sqlite3_column_bytes(self, i);
+        }
+    }
+    sq_pushinteger(v, total_size);
+    return 1;
+}
+
+static SQRESULT sq_sqlite3_stmt_colsSizeAsArray(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<col_count; ++i)
+    {
+        sq_pushinteger(v, sqlite3_column_bytes(self, i));
+        sq_arrayset(v, -2, i);
+    }
     return 1;
 }
 
@@ -1161,23 +1292,29 @@ static SQRegFunction sq_sqlite3_stmt_methods[] =
     _DECL_FUNC(next_row,  1, _SC("x"), SQFalse),
     _DECL_FUNC(colsAsArray,  1, _SC("x"), SQFalse),
     _DECL_FUNC(colsAsTable,  1, _SC("x"), SQFalse),
+    _DECL_FUNC(colsTypeAsArray,  1, _SC("x"), SQFalse),
+    _DECL_FUNC(colsDeclTypeAsArray,  1, _SC("x"), SQFalse),
     _DECL_FUNC(col_count,  1, _SC("x"), SQFalse),
     _DECL_FUNC(col_name,  2, _SC("xi"), SQFalse),
+    _DECL_FUNC(colsNameAsArray,  1, _SC("x"), 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(asJsonArray,  -1, _SC("x"), SQFalse),
-    _DECL_FUNC(asJsonObject,  1, _SC("x"), SQFalse),
+    _DECL_FUNC(asSleArray,  -1, _SC("xx"), SQFalse),
+    //todo manage/skip blobs for json
+    _DECL_FUNC(asJsonArray,  -1, _SC("xbx"), SQFalse),
+    _DECL_FUNC(asJsonObject,  -1, _SC("xx"), 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),
+    _DECL_FUNC(all_blobs_size,  1, _SC("x"), SQFalse),
+    _DECL_FUNC(colsSizeAsArray,  1, _SC("x"), SQFalse),
     {0,0}
 };
 #undef _DECL_FUNC
@@ -1246,7 +1383,8 @@ static SQRESULT sq_sqlite3_close_release(HSQUIRRELVM v, sq_sqlite3_sdb *sdb)
         {
         	//do no close statements because garbage collector will do it
         	//on MacOSX we get segfaults finalizing statements here
-            //sqlite3_finalize(statement);
+            printf("sq_sqlite3_close_release:stmt:%s\n", sqlite3_sql(statement));
+            sqlite3_finalize(statement);
             count++;
         }
         if (count) return sq_throwerror(v, _SC("closing database with %d statements not closed."), count);
@@ -1283,6 +1421,8 @@ static SQRESULT sq_sqlite3_close_release(HSQUIRRELVM v, sq_sqlite3_sdb *sdb)
             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->update_hook_cb);
+            sq_release(sdb->v, &sdb->update_hook_udata);
             sq_release(sdb->v, &sdb->null_value);
 
             sq_free(sdb, sizeof(sq_sqlite3_sdb));
@@ -1318,6 +1458,8 @@ static SQRESULT sq_sqlite3_constructor(HSQUIRRELVM v)
     sq_resetobject(&sdb->progress_udata);
     sq_resetobject(&sdb->trace_cb);
     sq_resetobject(&sdb->trace_udata);
+    sq_resetobject(&sdb->update_hook_cb);
+    sq_resetobject(&sdb->update_hook_udata);
     sq_resetobject(&sdb->null_value);
 
     sq_setinstanceup(v, 1, sdb);
@@ -1677,7 +1819,79 @@ static SQRESULT sq_sqlite3_trace(HSQUIRRELVM v)
         }
 
         /* set trace callback */
-        sqlite3_trace(self, db_trace_callback, sdb);
+        if(_top_ > 3)
+        {
+            SQBool b;
+            sq_getbool(v, 4, &b);
+            sqlite3_trace_v2(self, db_trace_callback, sdb, b == SQTrue);
+        }
+        else sqlite3_trace(self, db_trace_callback, sdb);
+    }
+
+    return 0;
+}
+
+/*
+** update hook callback:
+** Params: database, callback function, userdata
+**
+** callback function:
+** Params: userdata, sql
+*/
+static void db_update_hook_callback(void *user, int update_type,
+                char const *db_name, char const *table_name, sqlite3_int64 rowid)
+{
+    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->update_hook_cb);
+    sq_pushroottable(v);
+    sq_pushobject(v, sdb->update_hook_udata);
+    sq_pushinteger(v, update_type);
+    sq_pushstring(v, db_name, -1);
+    sq_pushstring(v, table_name, -1);
+    sq_pushinteger(v, rowid);
+
+    /* call squirrel function */
+    sq_call(v, 6, SQFalse, SQFalse);
+    /* ignore any error generated by this function */
+
+    sq_settop(v, top);
+}
+
+static SQRESULT sq_sqlite3_update_hook(HSQUIRRELVM v)
+{
+    SQ_FUNC_VARS(v);
+    GET_sqlite3_INSTANCE();
+
+    if (_top_ < 2 || sq_gettype(v, 2) == OT_NULL)
+    {
+        sq_release(v, &sdb->update_hook_cb);
+        sq_release(v, &sdb->update_hook_udata);
+
+        sq_resetobject(&sdb->update_hook_cb);
+        sq_resetobject(&sdb->update_hook_udata);
+
+        /* clear trace handler */
+        sqlite3_update_hook(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->update_hook_cb);
+        sq_addref(v, &sdb->update_hook_cb);
+        if(_top_ > 2)
+        {
+            sq_getstackobj(v, 3, &sdb->update_hook_udata);
+            sq_addref(v, &sdb->update_hook_udata);
+        }
+
+        /* set update_hook callback */
+        sqlite3_update_hook(self, db_update_hook_callback, sdb);
     }
 
     return 0;
@@ -2301,6 +2515,7 @@ static SQRegFunction sq_sqlite3_methods[] =
     _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(update_hook,  -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.")),
@@ -2422,6 +2637,9 @@ extern "C" {
         INT_CONST(v,SQLITE_NOTICE_RECOVER_WAL);
         INT_CONST(v,SQLITE_NOTICE_RECOVER_ROLLBACK);
         INT_CONST(v,SQLITE_WARNING_AUTOINDEX);
+        INT_CONST(v,SQLITE_INSERT);
+        INT_CONST(v,SQLITE_UPDATE);
+        INT_CONST(v,SQLITE_DELETE);
         //push sqlite3_NULL as a member
         sq_pushstring(v, nullName,-1);
         sq_pushobject(v, sqlite3_NULL);
@@ -2434,12 +2652,19 @@ extern "C" {
         sq_settypetag(v,-1,(void*)SQLite3_Stmt_TAG);
         sq_insert_reg_funcs(v, sq_sqlite3_stmt_methods);
 
+        INT_CONST(v,SQLITE_INTEGER);
+        INT_CONST(v,SQLITE_FLOAT);
+        INT_CONST(v,SQLITE_NULL);
+        INT_CONST(v,SQLITE_TEXT);
+        INT_CONST(v,SQLITE_BLOB);
         INT_CONST(v,SQLITE_OK);
         INT_CONST(v,SQLITE_DONE);
         INT_CONST(v,SQLITE_ROW);
         INT_CONST(v,AS_STRING_ALWAYS);
         INT_CONST(v,NULL_AS_EMPTY_STR);
         INT_CONST(v,WITH_COL_NAMES);
+        INT_CONST(v,WITH_BLOB_AS_NULL);
+        INT_CONST(v,WITH_SQ_NULL);
 
         //push sqlite3_NULL as a member
         sq_pushstring(v, nullName,-1);

+ 59 - 0
SquiLu-ext/sq_sqlite3.h

@@ -0,0 +1,59 @@
+#ifndef SQ_SQLITE3_H
+#define SQ_SQLITE3_H
+
+#include "squirrel.h"
+#include "sqlite3.h"
+#include <string.h>
+#include <stdio.h>
+#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 update_hook_cb;       /* update hook callback */
+    HSQOBJECT update_hook_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");
+
+#endif // SQ_SQLITE3_H