# HG changeset patch # User Olaf Wintermann # Date 1733669163 -3600 # Node ID 69ea9040d896e99e78dd8e87262346c0e9f5feb4 # Parent 4c12c95f4846ffe2c4d4d57e68c8adcb57f6bedd add DB abstraction layer diff -r 4c12c95f4846 -r 69ea9040d896 dbutils/Makefile --- a/dbutils/Makefile Sat Dec 07 23:10:03 2024 +0100 +++ b/dbutils/Makefile Sun Dec 08 15:46:03 2024 +0100 @@ -33,6 +33,7 @@ SRC += class.c SRC += field.c SRC += db.c +SRC += object.c SRC += sqlite.c OBJ = $(SRC:%.c=../build/dbutils/%$(OBJ_EXT)) diff -r 4c12c95f4846 -r 69ea9040d896 dbutils/db.c --- a/dbutils/db.c Sat Dec 07 23:10:03 2024 +0100 +++ b/dbutils/db.c Sun Dec 08 15:46:03 2024 +0100 @@ -27,4 +27,44 @@ */ #include "db.h" +#include "dbutils/db.h" +int dbuQuerySetSQL(DBUQuery *q, const char *sql) { + return q->setSQL(q, sql); +} + +int dbuQuerySetParamString(DBUQuery *q, int index, cxstring str) { + return q->setParamString(q, index, str); +} + +int dbuQuerySetParamInt(DBUQuery *q, int index, int i) { + return q->setParamInt(q, index, i); +} + +int dbuQuerySetParamInt64(DBUQuery *q, int index, int64_t i) { + return q->setParamInt64(q, index, i); +} + +int dbuQuerySetParamDouble(DBUQuery *q, int index, double d) { + return q->setParamDouble(q, index, d); +} + +int dbuQuerySetParamNull(DBUQuery *q, int index) { + return q->setParamNull(q, index); +} + +int dbuQuerySetParamBytes(DBUQuery *q, int index, void *bytes, int len) { + return q->setParamBytes(q, index, bytes, len); +} + +int dbuQueryExec(DBUQuery *q) { + return q->exec(q); +} + +DBUResult* dbuQueryGetResult(DBUQuery *q) { + return q->getResult(q); +} + +void dbuQueryFree(DBUQuery *q) { + q->free(q); +} diff -r 4c12c95f4846 -r 69ea9040d896 dbutils/db.h --- a/dbutils/db.h Sat Dec 07 23:10:03 2024 +0100 +++ b/dbutils/db.h Sun Dec 08 15:46:03 2024 +0100 @@ -30,15 +30,12 @@ #define DBU_DB_H #include "dbutils/dbutils.h" +#include "dbutils/db.h" #ifdef __cplusplus extern "C" { #endif -typedef struct DBUFieldMapping { - DBUField *field; - int result_index; -} DBUFieldMapping; #ifdef __cplusplus diff -r 4c12c95f4846 -r 69ea9040d896 dbutils/dbutils/db.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbutils/dbutils/db.h Sun Dec 08 15:46:03 2024 +0100 @@ -0,0 +1,117 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBDBU_DB_H +#define LIBDBU_DB_H + +#include "dbutils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DBUConnection DBUConnection; +typedef struct DBUQuery DBUQuery; +typedef struct DBUResult DBUResult; + +typedef enum DBUFieldType DBUFieldType; + +enum DBUFieldType { + DBU_FIELD_NULL = 0, + DBU_FIELD_TEXT, + DBU_FIELD_INT, + DBU_FIELD_DOUBLE, + DBU_FIELD_BINARY +}; + +typedef struct DBUBytes { + const unsigned char *bytes; + size_t length; +} DBUBytes; + +struct DBUConnection { + DBUQuery* (*createQuery)(DBUConnection *connection, const CxAllocator *a); + int (*isActive)(DBUConnection *connection); + void (*free)(DBUConnection *connection); + void *data; +}; + +struct DBUQuery { + int (*setSQL)(DBUQuery *q, const char *sql); + int (*setParamString)(DBUQuery *q, int index, cxstring str); + int (*setParamInt)(DBUQuery *q, int index, int i); + int (*setParamInt64)(DBUQuery *q, int index, int64_t i); + int (*setParamDouble)(DBUQuery *q, int index, double d); + int (*setParamNull)(DBUQuery *q, int index); + int (*setParamBytes)(DBUQuery *q, int index, void *bytes, int len); + int (*exec)(DBUQuery *q); + DBUResult* (*getResult)(DBUQuery *q); + void (*free)(DBUQuery *q); + DBUConnection *connection; + const CxAllocator *allocator; +}; + +struct DBUResult { + int (*optional_numRows)(DBUResult *result); + int (*numFields)(DBUResult *result); + int (*hasData)(DBUResult *result); + int (*isOk)(DBUResult *result); + int (*nextRow)(DBUResult *result); + const char* (*fieldName)(DBUResult *result, int field); + DBUFieldType (*fieldType)(DBUResult *result, int field); + int (*isNull)(DBUResult *result, int field); + cxstring (*getText)(DBUResult *result, int field); + int64_t (*optional_getInt)(DBUResult *result, int field); + double (*optional_getDouble)(DBUResult *result, int field); + DBUBytes (*optional_getBinary)(DBUResult *result, int field); + void (*free)(DBUResult *result); + const CxAllocator *allocator; + int rowIndex; +}; + + +int dbuQuerySetSQL(DBUQuery *q, const char *sql); +int dbuQuerySetParamString(DBUQuery *q, int index, cxstring str); +int dbuQuerySetParamInt(DBUQuery *q, int index, int i); +int dbuQuerySetParamInt64(DBUQuery *q, int index, int64_t i); +int dbuQuerySetParamDouble(DBUQuery *q, int index, double d); +int dbuQuerySetParamNull(DBUQuery *q, int index); +int dbuQuerySetParamBytes(DBUQuery *q, int index, void *bytes, int len); +int dbuQueryExec(DBUQuery *q); +DBUResult* dbuQueryGetResult(DBUQuery *q); +void dbuQueryFree(DBUQuery *q); + + +CxList* dbuQuerySingleType(DBUContext *ctx, DBUQuery *query, const char *type); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBDBU_DB_H */ + diff -r 4c12c95f4846 -r 69ea9040d896 dbutils/dbutils/dbutils.h --- a/dbutils/dbutils/dbutils.h Sat Dec 07 23:10:03 2024 +0100 +++ b/dbutils/dbutils/dbutils.h Sun Dec 08 15:46:03 2024 +0100 @@ -47,6 +47,8 @@ typedef struct DBUClass DBUClass; typedef struct DBUField DBUField; +typedef struct DBUObjectQuery DBUObjectQuery; + typedef char* DBUObject; typedef int(*DBUFieldDefInitFunc)(DBUField *f, const CxAllocator *a, DBUObject obj); diff -r 4c12c95f4846 -r 69ea9040d896 dbutils/dbutils/sqlite.h --- a/dbutils/dbutils/sqlite.h Sat Dec 07 23:10:03 2024 +0100 +++ b/dbutils/dbutils/sqlite.h Sun Dec 08 15:46:03 2024 +0100 @@ -30,15 +30,15 @@ #define LIBDBU_SQLITE_H #include "dbutils.h" - +#include "db.h" #include #ifdef __cplusplus extern "C" { #endif -CxList *dbuSQLiteQuerySingleTable(DBUContext *ctx, sqlite3 *db, const char *type, const char *sql); - +DBUConnection* dbuSQLiteConnection(const char *filename); +DBUConnection* dbuSQLiteConnectionFromDB(sqlite3 *db, bool autoclose); #ifdef __cplusplus } diff -r 4c12c95f4846 -r 69ea9040d896 dbutils/object.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbutils/object.c Sun Dec 08 15:46:03 2024 +0100 @@ -0,0 +1,114 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +#include "object.h" + + +CxList* dbuQuerySingleType(DBUContext *ctx, DBUQuery *query, const char *type) { + DBUClass *cls = cxMapGet(ctx->classes, type); + if(!cls) { + return NULL; + } + + // execute sql + if(query->exec(query)) { + query->free(query); + return NULL; + } + + DBUResult *result = query->getResult(query); + if(!result) { + return NULL; + } + query->free(query); + + // prepare list + CxList *list = cxArrayListCreateSimple(CX_STORE_POINTERS, 16); + if(!list) { + result->free(result); + } + + // map result to class fields + int numcols = result->numFields(result); + DBUFieldMapping *fields = calloc(numcols, sizeof(DBUFieldMapping)); + if(!fields) { + result->free(result); + cxListDestroy(list); + return NULL; + } + int numfields = 0; + for(int i=0;ifields, result->fieldName(result, i)); + if(field) { + DBUFieldMapping mapping; + mapping.field = field; + mapping.index = i; + mapping.type = result->fieldType(result, i); + fields[numfields++] = mapping; + } + } + + const CxAllocator *a = cxDefaultAllocator; + + // get result + while(result->hasData(result)) { + void *obj = malloc(cls->obj_size); + if(!obj) { + break; + } + memset(obj, 0, sizeof(cls->obj_size)); + + for(int i=0;iisNull(result, field.index); + + if(isnull) { + field.field->initDefaultValue(field.field, a, obj); + } else { + cxstring text = result->getText(result, field.index); + field.field->initValue(field.field, a, obj, text.ptr, text.length); + } + } + + cxListAdd(list, obj); + + // load next row + result->nextRow(result); + } + + result->free(result); + + return list; +} diff -r 4c12c95f4846 -r 69ea9040d896 dbutils/object.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbutils/object.h Sun Dec 08 15:46:03 2024 +0100 @@ -0,0 +1,51 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DBU_OBJECT_H +#define DBU_OBJECT_H + +#include "dbutils/dbutils.h" +#include "dbutils/db.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DBUFieldMapping { + DBUField *field; + DBUFieldType type; + int index; +} DBUFieldMapping; + + +#ifdef __cplusplus +} +#endif + +#endif /* DBU_OBJECT_H */ + diff -r 4c12c95f4846 -r 69ea9040d896 dbutils/sqlite.c --- a/dbutils/sqlite.c Sat Dec 07 23:10:03 2024 +0100 +++ b/dbutils/sqlite.c Sun Dec 08 15:46:03 2024 +0100 @@ -36,77 +36,248 @@ #include -CxList *dbuSQLiteQuerySingleTable(DBUContext *ctx, sqlite3 *db, const char *type, const char *sql) { - DBUClass *cls = cxMapGet(ctx->classes, type); - if(!cls) { - return NULL; - } - - // execute sql - sqlite3_stmt *stmt; - int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0); + +DBUConnection* dbuSQLiteConnection(const char *filename) { + sqlite3 *db; + int rc = sqlite3_open(filename, &db); if(rc != SQLITE_OK) { return NULL; } - // prepare list - CxList *list = cxArrayListCreateSimple(CX_STORE_POINTERS, 16); - if(!list) { - sqlite3_finalize(stmt); - return NULL; + DBUConnection *conn = dbuSQLiteConnectionFromDB(db, true); + if(!conn) { + sqlite3_close(db); } - - // map result to class fields - int numcols = sqlite3_column_count(stmt); - DBUFieldMapping *fields = calloc(numcols, sizeof(DBUFieldMapping)); - if(!fields) { - sqlite3_finalize(stmt); - cxListDestroy(list); + return conn; +} + +DBUConnection* dbuSQLiteConnectionFromDB(sqlite3 *db, bool autoclose) { + DBUSQLiteConnection *connection = malloc(sizeof(DBUSQLiteConnection)); + if(!connection) { return NULL; } - int numfields = 0; - for(int i=0;ifields, sqlite3_column_name(stmt, i)); - if(field) { - DBUFieldMapping mapping; - mapping.field = field; - mapping.result_index = i; - fields[numfields++] = mapping; - } - } - - const CxAllocator *a = cxDefaultAllocator; + memset(connection, 0, sizeof(DBUSQLiteConnection)); + connection->connection.createQuery = dbuSQLiteConnCreateQuery; + connection->connection.isActive = dbuSQLiteConnIsActive; + connection->connection.free = dbuSQLiteConnFree; + connection->connection.data = db; + connection->db = db; + connection->autoclose = autoclose; - // get result - while(sqlite3_step(stmt) == SQLITE_ROW) { - void *obj = malloc(cls->obj_size); - if(!obj) { - break; - } - memset(obj, 0, sizeof(cls->obj_size)); - - for(int i=0;iinitDefaultValue(field.field, a, obj); - } else { - const char *text = (const char *)sqlite3_column_text(stmt, i); - int length = 0; - if(field.field->query_length) { - length = sqlite3_column_bytes(stmt, field.result_index); - } - - field.field->initValue(field.field, a, obj, text, length); - } - } - - cxListAdd(list, obj); + return (DBUConnection*)connection; +} + +/* ------------------- SQLite Connection Implementation -------------------*/ + +DBUQuery* dbuSQLiteConnCreateQuery(DBUConnection *connection, const CxAllocator *a) { + DBUSQLiteQuery *query = malloc(sizeof(DBUSQLiteQuery)); + if(!query) { + return NULL; } - sqlite3_finalize(stmt); - return list; + memset(query, 0, sizeof(DBUSQLiteQuery)); + query->query.allocator = a; + query->query.connection = connection; + query->query.setSQL = dbuSQLiteQuerySetSQL; + query->query.setParamString = dbuSQLiteQuerySetParamString; + query->query.setParamInt = dbuSQLiteQuerySetParamInt; + query->query.setParamInt64 = dbuSQLiteQuerySetParamInt64; + query->query.setParamDouble = dbuSQLiteQuerySetParamDouble; + query->query.setParamNull = dbuSQLiteQuerySetParamNull; + query->query.setParamBytes = dbuSQLiteQuerySetParamBytes; + query->query.exec = dbuSQLiteQueryExec; + query->query.getResult = dbuSQLiteQueryGetResult; + query->query.free = dbuSQLiteQueryFree; + query->db = connection->data; + query->ref = 1; + return (DBUQuery*)query; +} + +int dbuSQLiteConnIsActive(DBUConnection *connection) { + return 1; +} + +void dbuSQLiteConnFree(DBUConnection *connection) { + DBUSQLiteConnection *sqlite = (DBUSQLiteConnection*)connection; + if(sqlite->autoclose) { + sqlite3_close(sqlite->db); + } + free(connection); } +/* --------------------- SQLite Query Implementation ---------------------*/ + +int dbuSQLiteQuerySetSQL(DBUQuery *q, const char *sql) { + DBUSQLiteQuery *query = (DBUSQLiteQuery*)q; + if(query->stmt) { + return 1; + } + return sqlite3_prepare_v2(query->db, sql, -1, &query->stmt, 0) != SQLITE_OK; +} + +int dbuSQLiteQuerySetParamString(DBUQuery *q, int index, cxstring str) { + DBUSQLiteQuery *query = (DBUSQLiteQuery*)q; + return sqlite3_bind_text(query->stmt, index, str.ptr, str.length, NULL); +} + +int dbuSQLiteQuerySetParamInt(DBUQuery *q, int index, int i) { + DBUSQLiteQuery *query = (DBUSQLiteQuery*)q; + return sqlite3_bind_int(query->stmt, index, i); +} + +int dbuSQLiteQuerySetParamInt64(DBUQuery *q, int index, int64_t i) { + DBUSQLiteQuery *query = (DBUSQLiteQuery*)q; + return sqlite3_bind_int64(query->stmt, index, i); +} + +int dbuSQLiteQuerySetParamDouble(DBUQuery *q, int index, double d) { + DBUSQLiteQuery *query = (DBUSQLiteQuery*)q; + return sqlite3_bind_double(query->stmt, index, d); +} + +int dbuSQLiteQuerySetParamNull(DBUQuery *q, int index) { + DBUSQLiteQuery *query = (DBUSQLiteQuery*)q; + return sqlite3_bind_null(query->stmt, index); +} + +int dbuSQLiteQuerySetParamBytes(DBUQuery *q, int index, void *bytes, int len) { + DBUSQLiteQuery *query = (DBUSQLiteQuery*)q; + return sqlite3_bind_blob(query->stmt, index, bytes, len, NULL); +} + +int dbuSQLiteQueryExec(DBUQuery *q) { + DBUSQLiteQuery *query = (DBUSQLiteQuery*)q; + int step = sqlite3_step(query->stmt); + if(step == SQLITE_ROW || step == SQLITE_DONE) { + query->step = step; + return 0; + } + return 1; +} + +DBUResult* dbuSQLiteQueryGetResult(DBUQuery *q) { + DBUSQLiteQuery *query = (DBUSQLiteQuery*)q; + if(query->result) { + return (DBUResult*)query->result; + } + + DBUSQLiteResult *result = malloc(sizeof(DBUSQLiteResult)); + if(!result) { + return NULL; + } + memset(result, 0, sizeof(DBUSQLiteResult)); + query->ref++; + result->query = query; + query->result = result; + + result->result.optional_numRows = NULL; + result->result.numFields = dbuSQLiteResultNumFields; + result->result.hasData = dbuSQLiteResultHasData; + result->result.isOk = dbuSQLiteResultIsOk; + result->result.nextRow = dbuSQLiteResultNextRow; + result->result.fieldName = dbuSQLiteResultFieldName; + result->result.fieldType = dbuSQLiteResultFieldType; + result->result.isNull = dbuSQLiteResultIsNull; + result->result.getText = dbuSQLiteResultGetText; + result->result.optional_getInt = dbuSQLiteResultGetInt; + result->result.optional_getDouble = dbuSQLiteResultGetDouble; + result->result.optional_getBinary = dbuSQLiteResultGetBinary; + result->result.free = dbuSQLiteResultFree; + result->result.allocator = q->allocator; + result->stmt = query->stmt; + result->step = query->step; + + return (DBUResult*)result; +} + +void dbuSQLiteQueryFree(DBUQuery *q) { + DBUSQLiteQuery *query = (DBUSQLiteQuery*)q; + if(--query->ref > 0) { + return; + } + sqlite3_finalize(query->stmt); + free(query); +} + + +/* --------------------- SQLite Result Implementation ---------------------*/ + +int dbuSQLiteResultNumFields(DBUResult *result) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + return sqlite3_column_count(r->stmt); +} + +int dbuSQLiteResultHasData(DBUResult *result) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + return r->step == SQLITE_ROW; +} + +int dbuSQLiteResultIsOk(DBUResult *result) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + return r->step == SQLITE_DONE; +} + +int dbuSQLiteResultNextRow(DBUResult *result) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + r->step = sqlite3_step(r->stmt); + int done = r->step == SQLITE_DONE; + r->result.rowIndex++; + return done; +} + +const char* dbuSQLiteResultFieldName(DBUResult *result, int field) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + return sqlite3_column_name(r->stmt, field); +} + +DBUFieldType dbuSQLiteResultFieldType(DBUResult *result, int field) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + int type = sqlite3_column_type(r->stmt, field); + DBUFieldType dbuType; + switch(type) { + default: dbuType = DBU_FIELD_TEXT; break; + case SQLITE_INTEGER: dbuType = DBU_FIELD_INT; break; + case SQLITE_FLOAT: dbuType = DBU_FIELD_DOUBLE; break; + case SQLITE_TEXT: dbuType = DBU_FIELD_TEXT; break; + case SQLITE_BLOB: dbuType = DBU_FIELD_BINARY; break; + case SQLITE_NULL: dbuType = DBU_FIELD_NULL; break; + } + return dbuType; +} + +int dbuSQLiteResultIsNull(DBUResult *result, int field) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + return sqlite3_column_type(r->stmt, field) == SQLITE_NULL; +} + +cxstring dbuSQLiteResultGetText(DBUResult *result, int field) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + const char *s = (const char*)sqlite3_column_text(r->stmt, field); + size_t len = (size_t)sqlite3_column_bytes(r->stmt, field); + return cx_strn(s, len); +} + +int64_t dbuSQLiteResultGetInt(DBUResult *result, int field) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + return sqlite3_column_int64(r->stmt, field); +} + +double dbuSQLiteResultGetDouble(DBUResult *result, int field) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + return sqlite3_column_double(r->stmt, field); +} + +DBUBytes dbuSQLiteResultGetBinary(DBUResult *result, int field) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + const unsigned char *s = sqlite3_column_blob(r->stmt, field); + size_t len = (size_t)sqlite3_column_bytes(r->stmt, field); + return (DBUBytes) { s, len }; +} + +void dbuSQLiteResultFree(DBUResult *result) { + DBUSQLiteResult *r = (DBUSQLiteResult*)result; + r->query->query.free((DBUQuery*)r->query); + free(r); +} + #endif /* DBU_SQLITE */ diff -r 4c12c95f4846 -r 69ea9040d896 dbutils/sqlite.h --- a/dbutils/sqlite.h Sat Dec 07 23:10:03 2024 +0100 +++ b/dbutils/sqlite.h Sun Dec 08 15:46:03 2024 +0100 @@ -37,8 +37,58 @@ extern "C" { #endif +typedef struct DBUSQLiteQuery DBUSQLiteQuery; +typedef struct DBUSQLiteResult DBUSQLiteResult; + +typedef struct DBUSQLiteConnection { + DBUConnection connection; + sqlite3 *db; + bool autoclose; +} DBUSQLiteConnection; +struct DBUSQLiteQuery { + DBUQuery query; + sqlite3 *db; + sqlite3_stmt *stmt; + DBUSQLiteResult *result; + int step; + int ref; +}; +struct DBUSQLiteResult { + DBUResult result; + sqlite3_stmt *stmt; + DBUSQLiteQuery *query; + int step; +}; + +DBUQuery* dbuSQLiteConnCreateQuery(DBUConnection *connection, const CxAllocator *a) ; +int dbuSQLiteConnIsActive(DBUConnection *connection); +void dbuSQLiteConnFree(DBUConnection *connection); + +int dbuSQLiteQuerySetSQL(DBUQuery *q, const char *sql); +int dbuSQLiteQuerySetParamString(DBUQuery *q, int index, cxstring str); +int dbuSQLiteQuerySetParamInt(DBUQuery *q, int index, int i); +int dbuSQLiteQuerySetParamInt64(DBUQuery *q, int index, int64_t i); +int dbuSQLiteQuerySetParamDouble(DBUQuery *q, int index, double d); +int dbuSQLiteQuerySetParamNull(DBUQuery *q, int index); +int dbuSQLiteQuerySetParamBytes(DBUQuery *q, int index, void *bytes, int len); +int dbuSQLiteQueryExec(DBUQuery *q); +DBUResult* dbuSQLiteQueryGetResult(DBUQuery *q); +void dbuSQLiteQueryFree(DBUQuery *q); + +int dbuSQLiteResultNumFields(DBUResult *result); +int dbuSQLiteResultHasData(DBUResult *result); +int dbuSQLiteResultIsOk(DBUResult *result); +int dbuSQLiteResultNextRow(DBUResult *result); +const char* dbuSQLiteResultFieldName(DBUResult *result, int field); +DBUFieldType dbuSQLiteResultFieldType(DBUResult *result, int field); +int dbuSQLiteResultIsNull(DBUResult *result, int field); +cxstring dbuSQLiteResultGetText(DBUResult *result, int field); +int64_t dbuSQLiteResultGetInt(DBUResult *result, int field); +double dbuSQLiteResultGetDouble(DBUResult *result, int field); +DBUBytes dbuSQLiteResultGetBinary(DBUResult *result, int field); +void dbuSQLiteResultFree(DBUResult *result); #ifdef __cplusplus } diff -r 4c12c95f4846 -r 69ea9040d896 test/main.c --- a/test/main.c Sat Dec 07 23:10:03 2024 +0100 +++ b/test/main.c Sun Dec 08 15:46:03 2024 +0100 @@ -31,6 +31,7 @@ #include #include +#include const char *sql_create_table_person = "create table if not exists Person (" @@ -89,7 +90,10 @@ return 1; } - CxList *persons = dbuSQLiteQuerySingleTable(ctx, db, "person", "select * from Person;"); + DBUConnection *conn = dbuSQLiteConnectionFromDB(db, true); + DBUQuery *query = conn->createQuery(conn, NULL); + dbuQuerySetSQL(query, "select * from Person;"); + CxList *persons = dbuQuerySingleType(ctx, query, "person"); if(persons) { CxIterator i = cxListIterator(persons); cx_foreach(Person *, p, i) { @@ -100,7 +104,7 @@ fprintf(stderr, "Error\n"); } - sqlite3_close(db); + conn->free(conn); return 0; }