|
|
|
struct Blob { |
|
size_t len; |
|
char* data; |
|
}; |
|
|
|
INTEGER -> int64_t; |
|
REAL -> double; |
|
TEXT -> char*; |
|
// BLOB -> Blob |
|
|
|
class Statement { |
|
public: |
|
/** |
|
* Construct a query |
|
*/ |
|
Query(SQLite* db, const char* path); |
|
|
|
static const int param_count = {{PARAM_COUNT}}; |
|
static const char* params = {"p0", "p1", "p2"}; |
|
static const char* param_types = { INTEGER, FLOAT, TEXT, BLOB, NULL } |
|
|
|
static const int column_count = {{COLUMN_COUNT}}; |
|
static const char* columns = {"col0", "col1", "col2"}; |
|
static const char* column_types = { INTEGER, FLOAT, TEXT, BLOB, NULL }; |
|
|
|
int param_pos[param_count]; |
|
int column_pos[column_count]; |
|
static Query create(const char* query) { |
|
if (!check(query)) { |
|
throw XYZ; |
|
} |
|
for (int i=0; i<N; ++i) { |
|
int pos[i] = sqlite3_bind_parameter_index(pStmt, params[i]); |
|
if (pos[i] == 0) { |
|
// Error!!! |
|
} |
|
// Also check that all parameters have been bound at this point |
|
} |
|
|
|
if (sqlite3_column_count(pStmt) != column_count) { |
|
// Error!!! |
|
} |
|
|
|
// NOTE: SQLite is dynamically typed so this will not work in all |
|
// cases: |
|
for (int i=0; i<column_count; ++i) { |
|
// query_type may be NULL if the column is an expression as SQLite |
|
// is dynamically typed. See `sqlite3_column_decltype` in the |
|
// SQLite documentation for more information. |
|
const char* query_type = sqlite3_column_decltype(pStmt, i); |
|
if (query_type && strcmp(query_type, declared_types[i]) != 0) { |
|
// Error types don't match |
|
} |
|
} |
|
|
|
// Match up column number with name |
|
for (int i=0; i<column_count; ++i) { |
|
column_pos[i] = -1; |
|
} |
|
for (int i=0; i<column_count; ++i) { |
|
const char* name = sqlite3_column_name(sqlite3_stmt* pStmt, i); |
|
for (int j=0; j<column_count; ++j) { |
|
int matched = 0; |
|
if (strcmp(name, columns[j]) == 0) { |
|
if (column_pos[j] == -1) { |
|
matched = 1; |
|
column_pos[j] = i; |
|
break; |
|
} |
|
else { |
|
// Error: we've already assigned to this column |
|
} |
|
} |
|
if (!matched) { |
|
// Error: Don't have a column called this |
|
} |
|
} |
|
} |
|
} |
|
unsigned char* append_string_to_result(unsigned char** extra_data, int column) { |
|
unsigned char* out = *extra_data; |
|
const unsigned char* text = sqlite3_column_text(pStmt, column); |
|
unsigned bytes = sqlite3_column_bytes(pStmt, column); |
|
memcpy(out, text, bytes); |
|
*extra_data += bytes; |
|
return out; |
|
} |
|
/** |
|
* The data returned from this function must be freed with sqlite_free |
|
*/ |
|
Result* step() { |
|
// Work out how much additional space will be required |
|
int extra_bytes = 0; |
|
for (int i = 0; i < column_count; ++i) { |
|
if (column_types[i] = SQLITE_TEXT) { |
|
sqlite3_column_text(pStmt, column_pos[3]); |
|
int size = sqlite3_column_bytes(pStmt, column_pos[i]); |
|
extra_bytes += size; |
|
} |
|
} |
|
void* data = sqlite_malloc(sizeof(Result) + extra_bytes); |
|
if (!data) { |
|
errno = ENOMEM; |
|
goto fail; |
|
} |
|
unsigned char* extra_data = (unsigned char*) data + extra_bytes; |
|
Result* result = (Result*) result; |
|
|
|
result->col0 = sqlite3_column_double(pStmt, column_pos[0]); |
|
result->col1 = sqlite3_column_int64(pStmt, column_pos[1]); |
|
result->col2 = sqlite3_column_int(pStmt, column_pos[2]); |
|
result->col3 = append_string_to_result(&extra_data, column_pos[3]); |
|
|
|
return result; |
|
fail: |
|
sqlite_free(result); |
|
} |
|
|
|
void bind(T0 name0, T1 name1, T2 name2) { |
|
|
|
sqlite_bind_T0(pStmt, pos[0], name0); |
|
sqlite_bind_T1(pStmt, pos[1], name1); |
|
sqlite_bind_T2(pStmt, pos[2], name2); |
|
sqlite_bind_T3(pStmt, pos[3], name3); |
|
|
|
sqlite_bind_blob(pStmt, pos[0], |
|
sqlite_bind_double |
|
sqlite_bind_int64 |
|
sqlite_bind_text |
|
} |
|
void check |
|
|
|
void clear_bindings() { |
|
sqlite3_clear_bindings(pStmt); |
|
} |
|
void reset() { |
|
sqlite3_reset(pStmt); |
|
} |
|
~Query() { |
|
sqlite3_finalize(pStmt); |
|
} |
|
private: |
|
sqlite3_stmt* pStmt; |
|
}; |
|
|
|
|
|
Statement create(db, path) { |
|
sqlite3_stmt* pStmt; |
|
|
|
string sql = read_contents(path); |
|
|
|
sqlite_prepare_v2(db, sql.c_str(), sql.length(), pStmt, NULL); |
|
|
|
|
|
int sqlite3_prepare_v2( |
|
sqlite3 *db, /* Database handle */ |
|
const char *zSql, /* SQL statement, UTF-8 encoded */ |
|
int nByte, /* Maximum length of zSql in bytes. */ |
|
sqlite3_stmt **ppStmt, /* OUT: Statement handle */ |
|
const char **pzTail /* OUT: Pointer to unused portion of zSql */ |
|
); |
|
|
|
struct Result { |
|
const char* x; |
|
int64_t y; |
|
|
|
}; |
|
|
|
ResultIterator { |
|
std::auto_ptr<Result> operator*(); |
|
}; |