Files
sqlite3/sqlite3_helper.h
2020-01-27 09:05:11 +01:00

299 lines
6.0 KiB
C

#include "sqlite3.h"
// --- BEGIN HELPERS ---
typedef struct {
sqlite3* handle;
} SQLite;
typedef struct {
int tag;
union {
int i;
double f;
char* s;
};
} SQLiteColumn;
int SQLiteColumn_tag(SQLiteColumn* col) {
return col->tag;
}
int SQLiteColumn_from_int(SQLiteColumn col) {
return col.i;
}
double SQLiteColumn_from_float(SQLiteColumn col) {
return col.f;
}
char* SQLiteColumn_from_str(SQLiteColumn col) {
return col.s;
}
SQLiteColumn SQLiteColumn_nil() {
SQLiteColumn res;
res.tag = SQLITE_NULL;
return res;
}
SQLiteColumn SQLiteColumn_int(int i) {
SQLiteColumn res;
res.tag = SQLITE_INTEGER;
res.i = i;
return res;
}
SQLiteColumn SQLiteColumn_float(double f) {
SQLiteColumn res;
res.tag = SQLITE_FLOAT;
res.f = f;
return res;
}
SQLiteColumn SQLiteColumn_text(char* s) {
SQLiteColumn res;
res.tag = SQLITE_TEXT;
res.s = s;
return res;
}
SQLiteColumn SQLiteColumn_blob(char* s) {
SQLiteColumn res;
res.tag = SQLITE_BLOB;
res.s = s;
return res;
}
typedef struct {
int columns;
SQLiteColumn* data;
} SQLiteRow;
int SQLiteRow_length(SQLiteRow* row) {
return row->columns;
}
SQLiteColumn SQLiteRow_nth(SQLiteRow* row, int i) {
return row->data[i];
}
typedef struct {
int capacity;
int len;
SQLiteRow* rows;
} SQLiteRows;
SQLiteRow* SQLiteRows_next_row(SQLiteRows* rows) {
SQLiteRow* res;
if (rows->capacity <= rows->len) {
if (!(rows->capacity)) rows->capacity = 10;
else rows->capacity *= 2;
rows->rows = realloc(rows->rows, (rows->capacity)*sizeof(SQLiteRow));
}
res = (rows->rows)+(rows->len);
rows->len++;
return res;
}
void SQLiteRows_finalize(SQLiteRows* rows) {
rows->capacity = rows->len;
rows->rows = realloc(rows->rows, (rows->capacity)*sizeof(SQLiteRow));
}
SQLiteRows SQLiteRows_new_rows() {
SQLiteRows res;
res.len = 0;
res.capacity = 0;
res.rows = NULL;
return res;
}
typedef struct {
int is;
union {
const char* err;
SQLiteRows rows;
};
} SQLiteRes;
#define OK 0
#define ERR 1
int SQLiteRes_length(SQLiteRes* r) {
return r->rows.len;
}
SQLiteRow SQLiteRes_nth(SQLiteRes* r, int i) {
return r->rows.rows[i];
}
bool SQLiteRes_is_ok(SQLiteRes* r) {
return r->is == OK;
}
char* SQLiteRes_error(SQLiteRes r) {
return (char*)r.err;
}
// --- END HELPERS ---
SQLite SQLite3_init() {
SQLite res;
res.handle = NULL;
return res;
}
int SQLite3_open_c(SQLite* db, const char* filename) {
sqlite3* c;
int res = sqlite3_open(filename, &c);
db->handle = c;
return res;
}
const char* SQLite3_exec_internal(sqlite3_stmt* s, SQLiteRows* rows) {
int status;
int len;
const char* err = NULL;
int count = sqlite3_column_count(s);
do {
status = sqlite3_step(s);
if (status == SQLITE_ROW) {
SQLiteRow* row = SQLiteRows_next_row(rows);
row->columns = count;
row->data = CARP_MALLOC(count*sizeof(SQLiteColumn));
for (int i = 0; i < count; i++) {
SQLiteColumn* c = row->data+i;
c->tag = sqlite3_column_type(s, i);
switch(c->tag) {
case SQLITE_INTEGER:
c->i = sqlite3_column_int(s, i);
break;
case SQLITE_FLOAT:
c->f = sqlite3_column_double(s, i);
break;
case SQLITE_TEXT: {
len = sqlite3_column_bytes(s, i);
c->s = CARP_MALLOC(len+1);
memcpy(c->s, sqlite3_column_text(s, i), len);
c->s[len] = '\0';
break;
}
case SQLITE_BLOB: {
len = sqlite3_column_bytes(s, i);
c->s = CARP_MALLOC(len+1);
memcpy(c->s, sqlite3_column_blob(s, i), len);
c->s[len] = '\0';
break;
}
case SQLITE_NULL:
break;
}
}
}
} while (status == SQLITE_ROW);
if (status != SQLITE_DONE) {
sqlite3* db = sqlite3_db_handle(s);
err = sqlite3_errmsg(db);
}
SQLiteRows_finalize(rows);
return err;
}
static const char* SQLite3_exec_ignore(sqlite3_stmt* s) {
int status;
const char* ret = NULL;
do { status = sqlite3_step(s); } while (status == SQLITE_ROW);
/* Check for errors */
if (status != SQLITE_DONE) {
sqlite3* db = sqlite3_db_handle(s);
ret = sqlite3_errmsg(db);
}
return ret;
}
const char* SQLite3_bind(sqlite3_stmt* s, Array* p) {
int res;
const char* err = NULL;
for (int i = 0; i < p->len; i++) {
SQLiteColumn val = ((SQLiteColumn*)p->data)[i];
switch (val.tag) {
case SQLITE_NULL:
res = sqlite3_bind_null(s, i+1);
break;
case SQLITE_INTEGER:
res = sqlite3_bind_int(s, i+1, val.i);
break;
case SQLITE_FLOAT:
res = sqlite3_bind_double(s, i+1, val.f);
break;
case SQLITE_TEXT:
res = sqlite3_bind_text(s, i+1, val.s, strlen(val.s), SQLITE_STATIC);
break;
case SQLITE_BLOB:
res = sqlite3_bind_blob(s, i+1, val.s, strlen(val.s), SQLITE_STATIC);
break;
}
if (res != SQLITE_OK) {
sqlite3* db = sqlite3_db_handle(s);
err = sqlite3_errmsg(db);
}
if (err) break;
}
return err;
}
SQLiteRes SQLite3_exec_c(SQLite* db, const char* stmt, Array* p) {
sqlite3_stmt* s = NULL;
sqlite3_stmt* n = NULL;
const char* err;
SQLiteRes res;
res.is = OK;
res.rows = SQLiteRows_new_rows();
do {
if (sqlite3_prepare_v2(db->handle, stmt, -1, &n, &stmt) != SQLITE_OK) {
err = sqlite3_errmsg(db->handle);
goto err;
} else {
if (n) {
err = SQLite3_bind(n, p);
if (err) goto err;
}
}
if (s) {
err = n ? SQLite3_exec_ignore(s) : SQLite3_exec_internal(s, &(res.rows));
if (err) goto err;
}
if (s) sqlite3_finalize(s);
s = n;
n = NULL;
} while (s);
return res;
err:
if (s) sqlite3_finalize(s);
if (n) sqlite3_finalize(n);
res.is = ERR;
res.err = err;
return res;
}
void SQLite3_close_c(SQLite db) {
sqlite3_close_v2(db.handle);
}
char* SQLite3_error(SQLite db) {
return (char*)sqlite3_errmsg(db.handle);
}