fix copy-paste error that caused -DDEBUG build to fail
[librdf.sqlite.git] / rdf_storage_sqlite_mro.c
blob05bd4483afe2b2a872f4fea4246b0783b01cfd8d
1 //
2 // rdf_storage_sqlite_mro.c
3 //
4 // Created by Marcus Rohrmoser on 19.05.14.
5 //
6 // Copyright (c) 2014-2018, Marcus Rohrmoser mobile Software, http://mro.name/~me
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without modification, are permitted
10 // provided that the following conditions are met:
12 // 1. Redistributions of source code must retain the above copyright notice, this list of conditions
13 // and the following disclaimer.
15 // 2. The software must not be used for military or intelligence or related purposes nor
16 // anything that's in conflict with human rights as declared in http://www.un.org/en/documents/udhr/ .
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
19 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
21 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
24 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
25 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "rdf_storage_sqlite_mro.h"
30 #define NAMESPACE "http://purl.mro.name/librdf.sqlite/"
31 const char *LIBRDF_STORAGE_SQLITE_MRO = NAMESPACE;
33 const unsigned char *LIBRDF_STORAGE_SQLITE_MRO_FEATURE_SQL_CACHE_MASK = (unsigned char *)NAMESPACE "feature/sql/cache/mask";
34 const unsigned char *LIBRDF_STORAGE_SQLITE_MRO_FEATURE_SQLITE3_PROFILE = (unsigned char *)NAMESPACE "feature/sqlite3/profile";
35 const unsigned char *LIBRDF_STORAGE_SQLITE_MRO_FEATURE_SQLITE3_EXPLAIN_QUERY_PLAN = (unsigned char *)NAMESPACE "feature/sqlite3/explain_query_plan";
37 #define LIBRDF_NAMESPACE_XSD "http://www.w3.org/2000/10/XMLSchema#"
39 #include <stdlib.h>
40 // #include <stdio.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <redland.h>
44 #include <rdf_storage.h>
45 #include <sqlite3.h>
46 #include <stdint.h>
47 #include <stdbool.h>
48 #include <errno.h>
50 #if DEBUG
51 #undef NDEBUG
52 #else
53 #define NDEBUG 1
54 #endif
55 #include <assert.h>
57 #define array_length(a) ( sizeof(a) / sizeof( (a)[0] ) )
59 #pragma mark Basic Types & Constants
61 typedef sqlite3_uint64 hash_t;
62 static const hash_t NULL_ID = 0;
63 static inline bool isNULL_ID(const hash_t x)
65 return NULL_ID == x;
69 #define RET_ERROR 1
70 #define RET_OK 0
72 /** C-String type for URIs. */
73 typedef unsigned char *str_uri_t;
74 /** C-String type for blank identifiers. */
75 typedef unsigned char *str_blank_t;
76 /** C-String type for literal values. */
77 typedef unsigned char *str_lit_val_t;
78 /** C-String type for (xml) language ids. */
79 typedef char *str_lang_t;
82 /** index into synchronous_flags */
83 typedef enum {
84 SYNC_UNKONWN = -1,
85 SYNC_OFF = 0,
86 SYNC_NORMAL = 1,
87 SYNC_FULL = 2
88 } syncronous_flag_t;
89 static const char *const synchronous_flags[4] = {
90 "off", "normal", "full", NULL
93 typedef enum {
94 P_S_URI = 1 << 0,
95 P_S_BLANK = 1 << 1,
96 P_P_URI = 1 << 2,
97 P_O_URI = 1 << 3,
98 P_O_BLANK = 1 << 4,
99 P_O_TEXT = 1 << 5,
100 P_O_LANGUAGE = 1 << 6,
101 P_O_DATATYPE = 1 << 7,
102 P_C_URI = 1 << 8
103 } sql_find_param_t;
105 #define ALL_PARAMS ( (P_C_URI << 1) - 1 )
107 typedef struct
109 sqlite3 *db;
110 librdf_digest *digest;
112 const char *name;
113 bool is_new;
114 syncronous_flag_t synchronous;
115 bool in_transaction;
117 bool do_profile;
118 bool do_explain_query_plan;
119 sql_find_param_t sql_cache_mask;
121 // compiled statements, lazy init
122 sqlite3_stmt *stmt_txn_start;
123 sqlite3_stmt *stmt_txn_commit;
124 sqlite3_stmt *stmt_txn_rollback;
125 sqlite3_stmt *stmt_triple_find; // complete triples
126 sqlite3_stmt *stmt_triple_insert;
127 sqlite3_stmt *stmt_triple_delete;
129 sqlite3_stmt *stmt_size;
131 instance_t;
134 #pragma mark librdf convenience
137 #define LIBRDF_MALLOC(type, size) (type)malloc(size)
138 #define LIBRDF_CALLOC(type, size, count) (type)calloc(count, size)
139 #define LIBRDF_FREE(type, ptr) free( (type)ptr )
140 // #define LIBRDF_BAD_CAST(t, v) (t)(v)
143 static inline instance_t *get_instance(librdf_storage *storage)
145 return (instance_t *)librdf_storage_get_instance(storage);
149 static inline librdf_world *get_world(librdf_storage *storage)
151 return librdf_storage_get_world(storage);
155 static inline void free_hash(librdf_hash *hash)
157 if( hash )
158 librdf_free_hash(hash);
162 static inline librdf_node_type node_type(librdf_node *node)
164 return NULL == node ? LIBRDF_NODE_TYPE_UNKNOWN : librdf_node_get_type(node);
168 static inline librdf_uri *literal_type_uri(librdf_node *o)
170 if( NULL == o || LIBRDF_NODE_TYPE_LITERAL != node_type(o) )
171 return NULL;
172 return librdf_node_get_literal_value_datatype_uri(o);
176 static inline char *literal_language(librdf_node *o)
178 if( NULL == o || LIBRDF_NODE_TYPE_LITERAL != node_type(o) )
179 return NULL;
180 return librdf_node_get_literal_value_language(o);
184 /* Copy first 8 bytes of digest into 64bit using a method portable across big/little endianness.
186 static hash_t digest_hash(librdf_digest *digest)
188 assert(digest && "must be set");
189 librdf_digest_final(digest);
191 assert(librdf_digest_get_digest_length(digest) >= sizeof(hash_t) && "digest length too small");
192 const int byte_count = 8;
193 const int bit_per_byte = 8;
194 assert(byte_count == sizeof(hash_t) && "made for 8-byte (64-bit) hashes");
195 uint8_t *diges = (uint8_t *)librdf_digest_get_digest(digest);
196 sqlite3_uint64 ret = 0; // enforce unsigned
197 for( int i = byte_count - 1; i >= 0; i-- ) {
198 ret <<= bit_per_byte;
199 ret += diges[i];
201 assert(!isNULL_ID(ret) && "null hash");
202 return (hash_t)ret;
206 static hash_t hash_uri(librdf_uri *uri, librdf_digest *digest)
208 if( !uri )
209 return NULL_ID;
210 assert(digest && "digest must be set.");
211 size_t len = 0;
212 const unsigned char *s = librdf_uri_as_counted_string(uri, &len);
213 assert(s && "uri NULL");
214 assert(len && "uri length 0");
215 librdf_digest_init(digest);
216 librdf_digest_update(digest, s, len);
217 return digest_hash(digest);
221 static hash_t node_hash_uri(librdf_node *node, librdf_digest *digest)
223 return hash_uri(LIBRDF_NODE_TYPE_RESOURCE == node_type(node) ? librdf_node_get_uri(node) : NULL, digest);
227 static hash_t node_hash_blank(librdf_node *node, librdf_digest *digest)
229 if( LIBRDF_NODE_TYPE_BLANK != node_type(node) )
230 return NULL_ID;
231 assert(digest && "digest must be set.");
232 size_t len = 0;
233 unsigned char *b = librdf_node_get_counted_blank_identifier(node, &len);
234 assert(b && "blank NULL");
235 assert(len && "blank len 0");
236 librdf_digest_init(digest);
237 librdf_digest_update(digest, b, len);
238 return digest_hash(digest);
242 static hash_t node_hash_literal(librdf_node *node, librdf_digest *digest)
244 if( LIBRDF_NODE_TYPE_LITERAL != node_type(node) )
245 return NULL_ID;
246 assert(digest && "digest must be set.");
247 librdf_digest_init(digest);
249 size_t len = 0;
250 unsigned char *str = librdf_node_get_literal_value_as_counted_string(node, &len);
251 assert(str && "literal without value");
252 assert(strlen( (char *)str ) == len && "TODO: NUL terminate or limit length!");
253 librdf_digest_update(digest, str, len);
255 librdf_uri *uri = librdf_node_get_literal_value_datatype_uri(node);
256 if( uri ) {
257 size_t len = 0;
258 const unsigned char *str = librdf_uri_as_counted_string(uri, &len);
259 librdf_digest_update(digest, str, len);
261 const char *l = librdf_node_get_literal_value_language(node);
262 if( l )
263 librdf_digest_update( digest, (unsigned char *)l, strlen(l) );
264 return digest_hash(digest);
268 /** https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x
270 static inline hash_t hash_combine(const hash_t seed, const hash_t b)
272 // return seed ^ ( b + 0x9e3779b9 + (seed << 6) + (seed >> 2) );
273 // http://stackoverflow.com/a/4948967 and https://stackoverflow.com/questions/4948780/magic-number-in-boosthash-combine#comment35569848_4948967
274 // $ python -c "import math; print hex(int(2**64 / ((1 + math.sqrt(5)) / 2)))"
275 return seed ^ ( b + 0x9e3779b97f4a7800L + (seed << 6) + (seed >> 2) );
279 static hash_t hash_combine_stmt(const hash_t s_uri_id, const hash_t s_blank_id, const hash_t p_uri_id, const hash_t o_uri_id, const hash_t o_blank_id, const hash_t o_lit_id, const hash_t c_uri_id)
281 hash_t stmt_id = NULL_ID;
282 stmt_id = hash_combine(stmt_id, s_uri_id);
283 stmt_id = hash_combine(stmt_id, s_blank_id);
284 stmt_id = hash_combine(stmt_id, p_uri_id);
285 stmt_id = hash_combine(stmt_id, o_uri_id);
286 stmt_id = hash_combine(stmt_id, o_blank_id);
287 stmt_id = hash_combine(stmt_id, o_lit_id);
288 // stmt_id ^= o_type_id;
289 stmt_id = hash_combine(stmt_id, c_uri_id);
290 return stmt_id;
294 static hash_t stmt_hash(librdf_statement *stmt, librdf_node *context_node, librdf_digest *digest)
296 if( !stmt )
297 return NULL_ID;
299 librdf_node *s = librdf_statement_get_subject(stmt);
300 librdf_node *p = librdf_statement_get_predicate(stmt);
301 librdf_node *o = librdf_statement_get_object(stmt);
303 return hash_combine_stmt( node_hash_uri(s, digest), node_hash_blank(s, digest), node_hash_uri(p, digest), node_hash_uri(o, digest), node_hash_blank(o, digest), node_hash_literal(o, digest), node_hash_uri(context_node, digest) );
307 #pragma mark Sqlite Debug/Profile
310 /* https://sqlite.org/eqp.html#section_2
311 ** Argument pStmt is a prepared SQL statement. This function compiles
312 ** an EXPLAIN QUERY PLAN command to report on the prepared statement,
313 ** and prints the report to stdout using printf().
315 static int printExplainQueryPlan(sqlite3_stmt *pStmt)
317 const char *zSql = sqlite3_sql(pStmt);
318 if( NULL == zSql ) return SQLITE_ERROR;
320 char *zExplain = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zSql);
321 if( NULL == zExplain ) return SQLITE_NOMEM;
323 sqlite3_stmt *pExplain; /* Compiled EXPLAIN QUERY PLAN command */
324 const int rc = sqlite3_prepare_v2(sqlite3_db_handle(pStmt), zExplain, -1, &pExplain, 0);
325 sqlite3_free(zExplain);
326 if( SQLITE_OK != rc ) return rc;
328 while( SQLITE_ROW == sqlite3_step(pExplain) ) {
329 const int iSelectid = sqlite3_column_int(pExplain, 0);
330 const int iOrder = sqlite3_column_int(pExplain, 1);
331 const int iFrom = sqlite3_column_int(pExplain, 2);
332 const char *zDetail = (const char *)sqlite3_column_text(pExplain, 3);
334 printf("%d %d %d %s\n", iSelectid, iOrder, iFrom, zDetail);
337 return sqlite3_finalize(pExplain);
341 #if 0
342 // http://stackoverflow.com/a/6618833
343 static void trace(void *context, const char *sql)
345 fprintf(stderr, "Query SQL: %s\n", sql);
349 #endif
352 // http://stackoverflow.com/a/6618833
353 static void profile(void *context, const char *sql, const sqlite3_uint64 ns)
355 // librdf_storage *storage = (librdf_storage *)context;
356 // instance_t *db_ctx = get_instance(storage);
357 fprintf(stderr, "dt=%llu ns Query SQL: %s\n", ns, sql);
361 #pragma mark Sqlite Convenience
364 /** Sqlite Result Code, e.g. SQLITE_OK */
365 typedef int sqlite_rc_t;
367 static sqlite_rc_t log_error(sqlite3 *db, const char *sql, const sqlite_rc_t rc)
369 if( SQLITE_OK != rc ) {
370 const char *errmsg = sqlite3_errmsg(db);
371 librdf_log(NULL, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "SQLite SQL error - %s (%d)\n%s", errmsg, rc, sql);
373 return rc;
377 static sqlite3_stmt *prep_stmt(sqlite3 *db, sqlite3_stmt **stmt_p, const char *zSql)
379 assert(db && "db handle is NULL");
380 assert(stmt_p && "statement is NULL");
381 assert(zSql && "SQL is NULL");
382 if( *stmt_p ) {
383 assert(0 == strcmp(sqlite3_sql(*stmt_p), zSql) && "sql changed during compilation.");
384 const sqlite_rc_t rc0 = sqlite3_reset(*stmt_p);
385 assert(SQLITE_OK == rc0 && "couldn't reset SQL statement");
386 const sqlite_rc_t rc1 = sqlite3_clear_bindings(*stmt_p);
387 assert(SQLITE_OK == rc1 && "couldn't reset SQL statement");
388 } else {
389 const char *remainder = NULL;
390 const int len_zSql = (int)strlen(zSql) + 1;
391 const sqlite_rc_t rc0 = log_error( db, zSql, sqlite3_prepare_v2(db, zSql, len_zSql, stmt_p, &remainder) );
392 assert(SQLITE_OK == rc0 && "couldn't compile SQL statement");
393 assert('\0' == *remainder && "had remainder");
395 assert(*stmt_p && "statement is NULL");
396 return *stmt_p;
400 static void finalize_stmt(sqlite3_stmt **pStmt)
402 if( NULL == *pStmt )
403 return;
404 sqlite3_reset(*pStmt);
405 const sqlite_rc_t rc = sqlite3_finalize(*pStmt);
406 assert(SQLITE_OK == rc && "couldn't properly sqlite3_finalize");
407 *pStmt = NULL;
411 static sqlite_rc_t exec_stmt(sqlite3 *db, const char *sql)
413 char *errmsg = NULL;
414 const sqlite_rc_t rc = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
415 sqlite3_free(errmsg);
416 return rc;
420 static inline sqlite_rc_t bind_int(sqlite3_stmt *stmt, const char *name, const hash_t _id)
422 assert(stmt && "stmt mandatory");
423 assert(name && "name mandatory");
424 const int idx = sqlite3_bind_parameter_index(stmt, name);
425 return 0 == idx ? SQLITE_OK : sqlite3_bind_int64(stmt, idx, _id);
429 static inline sqlite_rc_t bind_text(sqlite3_stmt *stmt, const char *name, const unsigned char *text, const size_t text_len)
431 assert(stmt && "stmt mandatory");
432 assert(name && "name mandatory");
433 const int idx = sqlite3_bind_parameter_index(stmt, name);
434 return 0 == idx ? SQLITE_OK : ( NULL == text ? sqlite3_bind_null(stmt, idx) : sqlite3_bind_text(stmt, idx, (const char *)text, (int)text_len, SQLITE_STATIC) );
438 static inline sqlite_rc_t bind_null(sqlite3_stmt *stmt, const char *name)
440 return bind_text(stmt, name, NULL, 0);
444 static sqlite_rc_t bind_uri_id(sqlite3_stmt *stmt, librdf_digest *digest, const char *name, librdf_uri *uri, hash_t *value)
446 if( uri ) {
447 const hash_t v = hash_uri(uri, digest);
448 if( value ) *value = v;
449 return bind_int(stmt, name, v);
451 return bind_null(stmt, name);
455 static sqlite_rc_t bind_uri(sqlite3_stmt *stmt, const char *name, librdf_uri *uri)
457 if( uri ) {
458 size_t len = 0;
459 const unsigned char *str = librdf_uri_as_counted_string(uri, &len);
460 return bind_text(stmt, name, str, len);
462 return bind_null(stmt, name);
466 static sqlite_rc_t bind_node_uri_id(sqlite3_stmt *stmt, librdf_digest *digest, const char *name, librdf_node *node, hash_t *value)
468 return bind_uri_id(stmt, digest, name, LIBRDF_NODE_TYPE_RESOURCE == node_type(node) ? librdf_node_get_uri(node) : NULL, value);
472 static sqlite_rc_t bind_node_uri(sqlite3_stmt *stmt, const char *name, librdf_node *node)
474 return bind_uri(stmt, name, LIBRDF_NODE_TYPE_RESOURCE == node_type(node) ? librdf_node_get_uri(node) : NULL);
478 static sqlite_rc_t bind_node_blank_id(sqlite3_stmt *stmt, librdf_digest *digest, const char *name, librdf_node *node, hash_t *value)
480 if( LIBRDF_NODE_TYPE_BLANK == node_type(node) ) {
481 const hash_t v = node_hash_blank(node, digest);
482 if( value ) *value = v;
483 return bind_int(stmt, name, v);
485 return bind_null(stmt, name);
489 static sqlite_rc_t bind_node_blank(sqlite3_stmt *stmt, const char *name, librdf_node *node)
491 if( LIBRDF_NODE_TYPE_BLANK == node_type(node) ) {
492 size_t len = 0;
493 const unsigned char *str = librdf_node_get_counted_blank_identifier(node, &len);
494 return bind_text(stmt, name, str, len);
496 return bind_null(stmt, name);
500 static sqlite_rc_t bind_node_lit_id(sqlite3_stmt *stmt, librdf_digest *digest, const char *name, librdf_node *node, hash_t *value)
502 if( LIBRDF_NODE_TYPE_LITERAL == node_type(node) ) {
503 const hash_t v = node_hash_literal(node, digest);
504 if( value ) *value = v;
505 return bind_int(stmt, name, v);
507 return bind_null(stmt, name);
511 static sqlite_rc_t bind_stmt(instance_t *db_ctx, librdf_statement *statement, librdf_node *context_node, sqlite3_stmt *stmt)
513 librdf_node *s = librdf_statement_get_subject(statement);
514 librdf_node *p = librdf_statement_get_predicate(statement);
515 librdf_node *o = librdf_statement_get_object(statement);
517 sqlite_rc_t rc = SQLITE_OK;
518 hash_t s_uri_id = NULL_ID;
519 hash_t s_blank_id = NULL_ID;
520 hash_t p_uri_id = NULL_ID;
521 hash_t o_uri_id = NULL_ID;
522 hash_t o_blank_id = NULL_ID;
523 hash_t o_lit_id = NULL_ID;
524 hash_t o_type_id = NULL_ID;
525 hash_t c_uri_id = NULL_ID;
526 if( SQLITE_OK != ( rc = bind_node_uri_id(stmt, db_ctx->digest, ":s_uri_id", s, &s_uri_id) ) ) return rc;
527 if( SQLITE_OK != ( rc = bind_node_uri(stmt, ":s_uri", s) ) ) return rc;
528 if( SQLITE_OK != ( rc = bind_node_blank_id(stmt, db_ctx->digest, ":s_blank_id", s, &s_blank_id) ) ) return rc;
529 if( SQLITE_OK != ( rc = bind_node_blank(stmt, ":s_blank", s) ) ) return rc;
530 if( SQLITE_OK != ( rc = bind_node_uri_id(stmt, db_ctx->digest, ":p_uri_id", p, &p_uri_id) ) ) return rc;
531 if( SQLITE_OK != ( rc = bind_node_uri(stmt, ":p_uri", p) ) ) return rc;
532 if( SQLITE_OK != ( rc = bind_node_uri_id(stmt, db_ctx->digest, ":o_uri_id", o, &o_uri_id) ) ) return rc;
533 if( SQLITE_OK != ( rc = bind_node_uri(stmt, ":o_uri", o) ) ) return rc;
534 if( SQLITE_OK != ( rc = bind_node_blank_id(stmt, db_ctx->digest, ":o_blank_id", o, &o_blank_id) ) ) return rc;
535 if( SQLITE_OK != ( rc = bind_node_blank(stmt, ":o_blank", o) ) ) return rc;
536 if( SQLITE_OK != ( rc = bind_node_lit_id(stmt, db_ctx->digest, ":o_lit_id", o, &o_lit_id) ) ) return rc;
537 if( LIBRDF_NODE_TYPE_LITERAL == node_type(o) ) {
538 if( SQLITE_OK != ( rc = bind_uri_id(stmt, db_ctx->digest, ":o_datatype_id", librdf_node_get_literal_value_datatype_uri(o), &o_type_id) ) ) return rc;
539 if( SQLITE_OK != ( rc = bind_uri( stmt, ":o_datatype", librdf_node_get_literal_value_datatype_uri(o) ) ) ) return rc;
540 char *l = librdf_node_get_literal_value_language(o);
541 if( l )
542 if( SQLITE_OK != ( rc = bind_text( stmt, ":o_language", (unsigned char *)l, strlen(l) ) ) ) return rc;
543 size_t len = 0;
544 const unsigned char *str = librdf_node_get_literal_value_as_counted_string(o, &len);
545 assert(strlen( (char *)str ) == len && "TODO: NUL terminate or limit length!");
546 if( SQLITE_OK != ( rc = bind_text(stmt, ":o_text", str, len) ) ) return rc;
549 if( SQLITE_OK != ( rc = bind_node_uri_id(stmt, db_ctx->digest, ":c_uri_id", context_node, &c_uri_id) ) ) return rc;
550 if( SQLITE_OK != ( rc = bind_node_uri(stmt, ":c_uri", context_node) ) ) return rc;
552 if( !librdf_statement_is_complete(statement) )
553 return SQLITE_OK;
555 const hash_t stmt_id = hash_combine_stmt(s_uri_id, s_blank_id, p_uri_id, o_uri_id, o_blank_id, o_lit_id, c_uri_id);
557 assert(stmt_hash(statement, context_node, db_ctx->digest) == stmt_id && "statement hash computation mismatch");
558 if( SQLITE_OK != ( rc = bind_int(stmt, ":stmt_id", stmt_id) ) ) return rc;
560 return SQLITE_OK;
564 static inline const str_uri_t column_uri_string(sqlite3_stmt *stmt, const int iCol)
566 return (str_uri_t)sqlite3_column_text(stmt, iCol);
570 static inline const str_blank_t column_blank_string(sqlite3_stmt *stmt, const int iCol)
572 return (str_blank_t)sqlite3_column_text(stmt, iCol);
576 static inline const str_lang_t column_language(sqlite3_stmt *stmt, const int iCol)
578 return (str_lang_t)sqlite3_column_text(stmt, iCol);
582 static sqlite_rc_t transaction_start(librdf_storage *storage)
584 instance_t *db_ctx = get_instance(storage);
585 if( db_ctx->in_transaction )
586 return SQLITE_MISUSE;
587 const sqlite_rc_t rc = sqlite3_step( prep_stmt(db_ctx->db, &(db_ctx->stmt_txn_start), "BEGIN IMMEDIATE TRANSACTION") );
588 db_ctx->in_transaction = SQLITE_DONE == rc;
589 assert(false != db_ctx->in_transaction && "transaction was not properly started");
590 return SQLITE_DONE == rc ? SQLITE_OK : rc;
594 static sqlite_rc_t transaction_commit(librdf_storage *storage, const sqlite_rc_t begin)
596 if( begin != SQLITE_OK )
597 return SQLITE_OK;
598 instance_t *db_ctx = get_instance(storage);
599 if( false == db_ctx->in_transaction )
600 return SQLITE_MISUSE;
601 const sqlite_rc_t rc = sqlite3_step( prep_stmt(db_ctx->db, &(db_ctx->stmt_txn_commit), "COMMIT TRANSACTION") );
602 db_ctx->in_transaction = !(SQLITE_DONE == rc);
603 assert(false == db_ctx->in_transaction && "transaction was not properly committed");
604 return SQLITE_DONE == rc ? SQLITE_OK : rc;
608 static sqlite_rc_t transaction_rollback(librdf_storage *storage, const sqlite_rc_t begin)
610 if( SQLITE_OK != begin )
611 return SQLITE_OK;
612 instance_t *db_ctx = get_instance(storage);
613 if( false == db_ctx->in_transaction )
614 return SQLITE_MISUSE;
615 const sqlite_rc_t rc = sqlite3_step( prep_stmt(db_ctx->db, &(db_ctx->stmt_txn_rollback), "ROLLBACK TRANSACTION") );
616 db_ctx->in_transaction = !(SQLITE_DONE == rc);
617 assert(false == db_ctx->in_transaction && "transaction was not properly rolled back");
618 return SQLITE_DONE == rc ? SQLITE_OK : rc;
622 #pragma mark -
625 #pragma mark Internal Implementation
628 /** insert_triple_sql + find_triples_sql
630 typedef enum {
631 IDX_S_URI = 9,
632 IDX_S_BLANK,
633 IDX_P_URI,
634 IDX_O_URI,
635 IDX_O_BLANK,
636 IDX_O_TEXT,
637 IDX_O_LANGUAGE,
638 IDX_O_DATATYPE,
639 IDX_C_URI
641 idx_triple_column_t;
644 static librdf_statement *find_statement(librdf_storage *storage, librdf_node *context_node, librdf_statement *statement, const bool create)
646 assert(statement && "statement must be set.");
647 assert(librdf_statement_is_complete(statement) && "statement must be complete.");
649 instance_t *db_ctx = get_instance(storage);
651 if( !create ) {
652 const hash_t stmt_id = stmt_hash(statement, context_node, db_ctx->digest);
653 assert(!isNULL_ID(stmt_id) && "mustn't be nil");
655 sqlite3_stmt *stmt = prep_stmt(db_ctx->db, &(db_ctx->stmt_triple_find), "SELECT id FROM triple_relations WHERE id = :stmt_id");
657 if( SQLITE_OK != bind_int(stmt, ":stmt_id", stmt_id) )
658 return NULL;
659 return SQLITE_ROW == sqlite3_step(stmt) ? statement : NULL;
661 const char insert_triple_sql[] = // generated via tools/sql2c.sh insert_triple.sql
662 "INSERT OR IGNORE INTO triples(" "\n" \
663 " id," "\n" \
664 " s_uri_id, s_uri," "\n" \
665 " s_blank_id, s_blank," "\n" \
666 " p_uri_id, p_uri," "\n" \
667 " o_uri_id, o_uri," "\n" \
668 " o_blank_id, o_blank," "\n" \
669 " o_lit_id, o_datatype_id, o_datatype, o_language, o_text," "\n" \
670 " c_uri_id, c_uri" "\n" \
671 ") VALUES (" "\n" \
672 " :stmt_id," "\n" \
673 " :s_uri_id, :s_uri," "\n" \
674 " :s_blank_id, :s_blank," "\n" \
675 " :p_uri_id, :p_uri," "\n" \
676 " :o_uri_id, :o_uri," "\n" \
677 " :o_blank_id, :o_blank," "\n" \
678 " :o_lit_id, :o_datatype_id, :o_datatype, :o_language, :o_text," "\n" \
679 " :c_uri_id, :c_uri" "\n" \
680 ")" "\n" \
683 sqlite3_stmt *stmt = prep_stmt(db_ctx->db, &(db_ctx->stmt_triple_insert), insert_triple_sql);
684 if( SQLITE_OK != bind_stmt(db_ctx, statement, context_node, stmt) )
685 return NULL;
686 if( db_ctx->do_explain_query_plan )
687 printExplainQueryPlan(stmt);
688 const sqlite_rc_t rc = sqlite3_step(stmt);
689 return SQLITE_DONE == rc ? statement : NULL;
693 #pragma mark -
695 #pragma mark Public Interface
698 int librdf_storage_set_feature_mro_bool(librdf_storage *storage, const unsigned char *feature, const bool value)
700 assert(storage && "storage must be set.");
701 assert(feature && "feature must be set.");
702 librdf_world *world = librdf_storage_get_world(storage);
703 assert(world && "world must be set.");
705 librdf_uri *uri_xsd_boolean = librdf_new_uri(world, (str_uri_t)LIBRDF_NAMESPACE_XSD "boolean");
706 assert(uri_xsd_boolean && "uri_xsd_boolean must be set.");
707 librdf_uri *uri_f = librdf_new_uri(world, feature);
708 assert(uri_f && "uri_f must be set.");
709 const str_lit_val_t v = (str_lit_val_t)(value ? "true" : "false");
710 // librdf_log(get_world(storage), 0, LIBRDF_LOG_DEBUG, LIBRDF_FROM_STORAGE, NULL, "librdf_storage_set_feature_mro_bool sets '%s'", v);
711 librdf_node *n = librdf_new_node_from_typed_literal(world, v, NULL, uri_xsd_boolean);
713 const int ret = librdf_storage_set_feature(storage, uri_f, n);
715 librdf_free_node(n);
716 librdf_free_uri(uri_f);
717 librdf_free_uri(uri_xsd_boolean);
719 return ret;
723 int librdf_storage_set_feature_mro_int(librdf_storage *storage, const unsigned char *feature, const int value)
725 assert(storage && "storage must be set.");
726 assert(feature && "feature must be set.");
727 librdf_world *world = librdf_storage_get_world(storage);
728 assert(world && "world must be set.");
730 librdf_uri *uri_xsd_integer = librdf_new_uri(world, (str_uri_t)LIBRDF_NAMESPACE_XSD "integer");
731 assert(uri_xsd_integer && "uri_xsd_integer must be set.");
732 librdf_uri *uri_f = librdf_new_uri(world, feature);
733 assert(uri_f && "uri_f must be set.");
734 char v[20];
735 snprintf(v, sizeof(v), "%d", value);
736 librdf_node *n = librdf_new_node_from_typed_literal(world, (str_lit_val_t)v, NULL, uri_xsd_integer);
738 const int ret = librdf_storage_set_feature(storage, uri_f, n);
740 librdf_free_node(n);
741 librdf_free_uri(uri_f);
742 librdf_free_uri(uri_xsd_integer);
744 return ret;
748 int librdf_storage_get_feature_mro_bool(librdf_storage *storage, const unsigned char *feature, bool *value)
750 assert(storage && "storage must be set.");
751 assert(feature && "feature must be set.");
752 librdf_world *world = librdf_storage_get_world(storage);
753 assert(world && "world must be set.");
755 librdf_uri *uri_f = librdf_new_uri(world, feature);
756 int err = RET_OK;
757 bool ret = false;
758 if( uri_f ) {
759 librdf_node *node = librdf_storage_get_feature(storage, uri_f);
760 if( node ) {
761 if( LIBRDF_NODE_TYPE_LITERAL == node_type(node) ) {
762 size_t len = 0;
763 const char *v = (char *)librdf_node_get_literal_value_as_counted_string(node, &len);
764 if( v ) {
765 // librdf_log(get_world(storage), 0, LIBRDF_LOG_DEBUG, LIBRDF_FROM_STORAGE, NULL, "librdf_storage_get_feature_mro_bool return '%s'", v);
766 if( 0 == strncmp("true", v, len) || 0 == strncmp("1", v, len) )
767 ret = true;
768 else if( 0 == strncmp("false", v, len) || 0 == strncmp("0", v, len) )
769 ret = false;
770 else
771 err = 4;
772 } else
773 err = 3;
774 } else
775 err = 2;
776 librdf_free_node(node);
777 } else
778 err = 1;
779 librdf_free_uri(uri_f);
781 if( value )
782 *value = ret;
783 return err;
787 int librdf_storage_get_feature_mro_int(librdf_storage *storage, const unsigned char *feature, int *value)
789 assert(storage && "storage must be set.");
790 assert(feature && "feature must be set.");
791 librdf_world *world = librdf_storage_get_world(storage);
792 assert(world && "world must be set.");
794 librdf_uri *uri_f = librdf_new_uri(world, feature);
795 int err = RET_OK;
796 long ret = RET_OK;
797 if( uri_f ) {
798 librdf_node *node = librdf_storage_get_feature(storage, uri_f);
799 if( node ) {
800 if( LIBRDF_NODE_TYPE_LITERAL == node_type(node) ) {
801 size_t len = 0;
802 const char *str = (char *)librdf_node_get_literal_value_as_counted_string(node, &len);
803 if( str ) {
804 assert(strlen(str) == len && "TODO: NUL terminate or limit length!");
805 char *end = NULL;
806 ret = strtol(str, &end, 10);
807 if( '\0' != *end )
808 err = 4;
809 } else
810 err = 3;
811 } else
812 err = 2;
813 librdf_free_node(node);
814 } else
815 err = 1;
816 librdf_free_uri(uri_f);
818 if( value )
819 *value = ret;
820 return err;
824 #pragma mark Lifecycle & Housekeeping
827 /** Create a new storage.
829 * Setup SQLIte connection instance but don't open yet.
831 static int pub_init(librdf_storage *storage, const char *name, librdf_hash *options)
833 if( !name ) {
834 free_hash(options);
835 return RET_ERROR;
838 instance_t *db_ctx = LIBRDF_CALLOC(instance_t *, sizeof(*db_ctx), 1);
839 if( !db_ctx ) {
840 free_hash(options);
841 return RET_ERROR;
843 // feature defaults
844 db_ctx->do_profile = false;
845 db_ctx->do_explain_query_plan = false;
846 db_ctx->sql_cache_mask = ALL_PARAMS;
848 librdf_storage_set_instance(storage, db_ctx);
849 const size_t name_len = strlen(name);
850 char *name_copy = LIBRDF_MALLOC(char *, name_len + 1);
851 if( !name_copy ) {
852 free_hash(options);
853 return RET_ERROR;
856 strncpy(name_copy, name, name_len + 1);
857 db_ctx->name = name_copy;
859 if( !( db_ctx->digest = librdf_new_digest(get_world(storage), "MD5") ) ) {
860 free_hash(options);
861 return RET_ERROR;
864 if( false != librdf_hash_get_as_boolean(options, "new") )
865 db_ctx->is_new = true; /* default is NOT NEW */
867 /* Redland default is "PRAGMA synchronous normal" */
868 db_ctx->synchronous = SYNC_NORMAL;
870 char *synchronous = synchronous = librdf_hash_get(options, "synchronous");
871 if( synchronous ) {
872 for( int i = 0; synchronous_flags[i]; i++ ) {
873 if( !strcmp(synchronous, synchronous_flags[i]) ) {
874 db_ctx->synchronous = i;
875 break;
878 LIBRDF_FREE(char *, synchronous);
881 free_hash(options);
882 return RET_OK;
886 static void pub_terminate(librdf_storage *storage)
888 instance_t *db_ctx = get_instance(storage);
889 if( NULL == db_ctx )
890 return;
891 if( db_ctx->name )
892 LIBRDF_FREE(char *, (void *)db_ctx->name);
893 if( db_ctx->digest )
894 librdf_free_digest(db_ctx->digest);
896 LIBRDF_FREE(instance_t *, db_ctx);
900 static int pub_close(librdf_storage *storage)
902 instance_t *db_ctx = get_instance(storage);
903 if( !db_ctx->db )
904 return RET_OK;
906 finalize_stmt( &(db_ctx->stmt_txn_start) );
907 finalize_stmt( &(db_ctx->stmt_txn_commit) );
908 finalize_stmt( &(db_ctx->stmt_txn_rollback) );
909 finalize_stmt( &(db_ctx->stmt_triple_find) );
910 finalize_stmt( &(db_ctx->stmt_triple_insert) );
911 finalize_stmt( &(db_ctx->stmt_triple_delete) );
913 finalize_stmt( &(db_ctx->stmt_size) );
915 const sqlite_rc_t rc = sqlite3_close(db_ctx->db);
916 if( SQLITE_OK == rc ) {
917 db_ctx->db = NULL;
918 return RET_OK;
920 char *errmsg = (char *)sqlite3_errmsg(db_ctx->db);
921 librdf_log(get_world(storage), 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "SQLite database %s close failed - %s", db_ctx->name, errmsg);
922 return rc;
926 static int pub_open(librdf_storage *storage, librdf_model *model)
928 instance_t *db_ctx = get_instance(storage);
930 const bool file_exists = ( 0 == access(db_ctx->name, F_OK) );
931 if( db_ctx->is_new && file_exists )
932 unlink(db_ctx->name);
934 // open DB
935 assert( (NULL == db_ctx->db) && "db handle mustn't be set by now" );
936 db_ctx->db = NULL;
938 const sqlite_rc_t rc = sqlite3_open(db_ctx->name, &db_ctx->db);
939 if( SQLITE_OK != rc ) {
940 const char *errmsg = sqlite3_errmsg(db_ctx->db);
941 librdf_log(get_world(storage), 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "SQLite database %s open failed - %s", db_ctx->name, errmsg);
942 return rc;
945 // http://stackoverflow.com/a/6618833
946 if( db_ctx->do_profile ) {
947 sqlite3_profile(db_ctx->db, &profile, NULL);
948 // sqlite3_trace(db_ctx->db, &trace, NULL);
952 // set DB session PRAGMAs
953 if( SYNC_OFF <= db_ctx->synchronous ) {
954 char sql[250];
955 const size_t len = snprintf(sql, sizeof(sql) - 1, "PRAGMA synchronous=%s;", synchronous_flags[db_ctx->synchronous]);
956 assert(len < sizeof(sql) && "buffer too small.");
957 const sqlite_rc_t rc = exec_stmt(db_ctx->db, sql);
958 if( SQLITE_OK != rc ) {
959 pub_close(storage);
960 return rc;
964 const char *const sqls[] = {
965 "PRAGMA foreign_keys = ON;",
966 "PRAGMA recursive_triggers = ON;",
967 "PRAGMA encoding = 'UTF-8';",
968 NULL
970 for( int v = 0; sqls[v]; v++ ) {
971 const sqlite_rc_t rc = exec_stmt(db_ctx->db, sqls[v]);
972 if( SQLITE_OK != rc ) {
973 pub_close(storage);
974 return rc;
979 // check & update schema (run migrations)
981 sqlite_rc_t rc = SQLITE_OK;
982 sqlite3_stmt *stmt = NULL;
983 prep_stmt(db_ctx->db, &stmt, "PRAGMA user_version;");
984 if( SQLITE_ROW != ( rc = sqlite3_step(stmt) ) ) {
985 sqlite3_finalize(stmt);
986 pub_close(storage);
987 return rc;
989 const int schema_version = sqlite3_column_int(stmt, 0);
990 if( SQLITE_DONE != ( rc = sqlite3_step(stmt) ) ) {
991 sqlite3_finalize(stmt);
992 pub_close(storage);
993 return rc;
995 sqlite3_finalize(stmt);
997 const char *const migrations[] = {
998 // generated via tools/sql2c.sh sql/schema_mig_to_1.sql
999 "PRAGMA foreign_keys = ON;" "\n" \
1000 "PRAGMA recursive_triggers = ON;" "\n" \
1001 "PRAGMA encoding = 'UTF-8';" "\n" \
1002 " -- URIs for subjects and objects" "\n" \
1003 "CREATE TABLE so_uris (" "\n" \
1004 " id INTEGER PRIMARY KEY" "\n" \
1005 " ,uri TEXT NOT NULL -- UNIQUE -- redundant constraint (hash should do), could be dropped to save space" "\n" \
1006 ");" "\n" \
1007 " -- blank node IDs for subjects and objects" "\n" \
1008 "CREATE TABLE so_blanks (" "\n" \
1009 " id INTEGER PRIMARY KEY" "\n" \
1010 " ,blank TEXT NOT NULL -- UNIQUE -- redundant constraint (hash should do), could be dropped to save space" "\n" \
1011 ");" "\n" \
1012 " -- URIs for predicates" "\n" \
1013 "CREATE TABLE p_uris (" "\n" \
1014 " id INTEGER PRIMARY KEY" "\n" \
1015 " ,uri TEXT NOT NULL -- UNIQUE -- redundant constraint (hash should do), could be dropped to save space" "\n" \
1016 ");" "\n" \
1017 " -- URIs for literal types" "\n" \
1018 "CREATE TABLE t_uris (" "\n" \
1019 " id INTEGER PRIMARY KEY" "\n" \
1020 " ,uri TEXT NOT NULL -- UNIQUE -- redundant constraint (hash should do), could be dropped to save space" "\n" \
1021 ");" "\n" \
1022 " -- literal values" "\n" \
1023 "CREATE TABLE o_literals (" "\n" \
1024 " id INTEGER PRIMARY KEY" "\n" \
1025 " ,datatype_id INTEGER NULL REFERENCES t_uris(id)" "\n" \
1026 " ,language TEXT NULL" "\n" \
1027 " ,text TEXT NOT NULL" "\n" \
1028 ");" "\n" \
1029 " -- CREATE UNIQUE INDEX o_literals_index ON o_literals (text,language,datatype_id); -- redundant constraint (hash should do), could be dropped to save space" "\n" \
1030 " -- URIs for context" "\n" \
1031 "CREATE TABLE c_uris (" "\n" \
1032 " id INTEGER PRIMARY KEY" "\n" \
1033 " ,uri TEXT NOT NULL -- UNIQUE -- redundant constraint (hash should do), could be dropped to save space" "\n" \
1034 ");" "\n" \
1035 "CREATE TABLE triple_relations (" "\n" \
1036 " id INTEGER PRIMARY KEY" "\n" \
1037 " ,s_uri_id INTEGER NULL REFERENCES so_uris(id)" "\n" \
1038 " ,s_blank_id INTEGER NULL REFERENCES so_blanks(id)" "\n" \
1039 " ,p_uri_id INTEGER NOT NULL REFERENCES p_uris(id)" "\n" \
1040 " ,o_uri_id INTEGER NULL REFERENCES so_uris(id)" "\n" \
1041 " ,o_blank_id INTEGER NULL REFERENCES so_blanks(id)" "\n" \
1042 " ,o_lit_id INTEGER NULL REFERENCES o_literals(id)" "\n" \
1043 " ,c_uri_id INTEGER NULL REFERENCES c_uris(id)" "\n" \
1044 " , CONSTRAINT null_subject CHECK ( -- ensure uri/blank are mutually exclusive" "\n" \
1045 " (s_uri_id IS NOT NULL AND s_blank_id IS NULL) OR" "\n" \
1046 " (s_uri_id IS NULL AND s_blank_id IS NOT NULL)" "\n" \
1047 " )" "\n" \
1048 " , CONSTRAINT null_object CHECK ( -- ensure uri/blank/literal are mutually exclusive" "\n" \
1049 " (o_uri_id IS NOT NULL AND o_blank_id IS NULL AND o_lit_id IS NULL) OR" "\n" \
1050 " (o_uri_id IS NULL AND o_blank_id IS NOT NULL AND o_lit_id IS NULL) OR" "\n" \
1051 " (o_uri_id IS NULL AND o_blank_id IS NULL AND o_lit_id IS NOT NULL)" "\n" \
1052 " )" "\n" \
1053 ");" "\n" \
1054 " -- redundant constraint (hash should do), could be dropped to save space:" "\n" \
1055 " -- CREATE UNIQUE INDEX triple_relations_index ON triple_relations(s_uri_id,s_blank_id,p_uri_id,o_uri_id,o_blank_id,o_lit_id,c_uri_id);" "\n" \
1056 " -- optional indexes for lookup performance, mostly on DELETE." "\n" \
1057 "CREATE INDEX triple_relations_index_s_uri_id ON triple_relations(s_uri_id); -- WHERE s_uri_id IS NOT NULL;" "\n" \
1058 "CREATE INDEX triple_relations_index_s_blank_id ON triple_relations(s_blank_id); -- WHERE s_blank_id IS NOT NULL;" "\n" \
1059 "CREATE INDEX triple_relations_index_p_uri_id ON triple_relations(p_uri_id); -- WHERE p_uri_id IS NOT NULL;" "\n" \
1060 "CREATE INDEX triple_relations_index_o_uri_id ON triple_relations(o_uri_id); -- WHERE o_uri_id IS NOT NULL;" "\n" \
1061 "CREATE INDEX triple_relations_index_o_blank_id ON triple_relations(o_blank_id); -- WHERE s_blank_id IS NOT NULL;" "\n" \
1062 "CREATE INDEX triple_relations_index_o_lit_id ON triple_relations(o_lit_id); -- WHERE o_lit_id IS NOT NULL;" "\n" \
1063 "CREATE INDEX o_literals_index_datatype_id ON o_literals(datatype_id); -- WHERE datatype_id IS NOT NULL;" "\n" \
1064 "PRAGMA user_version=1;" "\n" \
1066 // generated via tools/sql2c.sh sql/schema_mig_to_2.sql
1067 "CREATE VIEW triples AS" "\n" \
1068 "SELECT" "\n" \
1069 " -- all *_id (hashes):" "\n" \
1070 " triple_relations.id AS id" "\n" \
1071 " ,s_uri_id" "\n" \
1072 " ,s_blank_id" "\n" \
1073 " ,p_uri_id" "\n" \
1074 " ,o_uri_id" "\n" \
1075 " ,o_blank_id" "\n" \
1076 " ,o_lit_id" "\n" \
1077 " ,o_literals.datatype_id AS o_datatype_id" "\n" \
1078 " ,c_uri_id" "\n" \
1079 " -- all joined values:" "\n" \
1080 " ,s_uris.uri AS s_uri" "\n" \
1081 " ,s_blanks.blank AS s_blank" "\n" \
1082 " ,p_uris.uri AS p_uri" "\n" \
1083 " ,o_uris.uri AS o_uri" "\n" \
1084 " ,o_blanks.blank AS o_blank" "\n" \
1085 " ,o_literals.text AS o_text" "\n" \
1086 " ,o_literals.language AS o_language" "\n" \
1087 " ,o_lit_uris.uri AS o_datatype" "\n" \
1088 " ,c_uris.uri AS c_uri" "\n" \
1089 "FROM triple_relations" "\n" \
1090 "LEFT OUTER JOIN so_uris AS s_uris ON triple_relations.s_uri_id = s_uris.id" "\n" \
1091 "LEFT OUTER JOIN so_blanks AS s_blanks ON triple_relations.s_blank_id = s_blanks.id" "\n" \
1092 "INNER JOIN p_uris AS p_uris ON triple_relations.p_uri_id = p_uris.id" "\n" \
1093 "LEFT OUTER JOIN so_uris AS o_uris ON triple_relations.o_uri_id = o_uris.id" "\n" \
1094 "LEFT OUTER JOIN so_blanks AS o_blanks ON triple_relations.o_blank_id = o_blanks.id" "\n" \
1095 "LEFT OUTER JOIN o_literals AS o_literals ON triple_relations.o_lit_id = o_literals.id" "\n" \
1096 "LEFT OUTER JOIN t_uris AS o_lit_uris ON o_literals.datatype_id = o_lit_uris.id" "\n" \
1097 "LEFT OUTER JOIN c_uris AS c_uris ON triple_relations.c_uri_id = c_uris.id" "\n" \
1098 ";" "\n" \
1099 "CREATE TRIGGER triples_insert INSTEAD OF INSERT ON triples" "\n" \
1100 "FOR EACH ROW BEGIN" "\n" \
1101 " -- subject uri/blank" "\n" \
1102 " INSERT OR IGNORE INTO so_uris (id,uri) VALUES (NEW.s_uri_id, NEW.s_uri);" "\n" \
1103 " INSERT OR IGNORE INTO so_blanks (id,blank) VALUES (NEW.s_blank_id, NEW.s_blank);" "\n" \
1104 " -- predicate uri" "\n" \
1105 " INSERT OR IGNORE INTO p_uris (id,uri) VALUES (NEW.p_uri_id, NEW.p_uri);" "\n" \
1106 " -- object uri/blank" "\n" \
1107 " INSERT OR IGNORE INTO so_uris (id,uri) VALUES (NEW.o_uri_id, NEW.o_uri);" "\n" \
1108 " INSERT OR IGNORE INTO so_blanks (id,blank) VALUES (NEW.o_blank_id, NEW.o_blank);" "\n" \
1109 " -- object literal" "\n" \
1110 " INSERT OR IGNORE INTO t_uris (id,uri) VALUES (NEW.o_datatype_id, NEW.o_datatype);" "\n" \
1111 " INSERT OR IGNORE INTO o_literals(id,datatype_id,language,text) VALUES (NEW.o_lit_id, NEW.o_datatype_id, NEW.o_language, NEW.o_text);" "\n" \
1112 " -- context uri" "\n" \
1113 " INSERT OR IGNORE INTO c_uris (id,uri) VALUES (NEW.c_uri_id, NEW.c_uri);" "\n" \
1114 " -- triple" "\n" \
1115 " INSERT INTO triple_relations(id, s_uri_id, s_blank_id, p_uri_id, o_uri_id, o_blank_id, o_lit_id, c_uri_id)" "\n" \
1116 " VALUES (NEW.id, NEW.s_uri_id, NEW.s_blank_id, NEW.p_uri_id, NEW.o_uri_id, NEW.o_blank_id, NEW.o_lit_id, NEW.c_uri_id);" "\n" \
1117 "END;" "\n" \
1118 "CREATE TRIGGER triples_delete INSTEAD OF DELETE ON triples" "\n" \
1119 "FOR EACH ROW BEGIN" "\n" \
1120 " -- triple" "\n" \
1121 " DELETE FROM triple_relations WHERE id = OLD.id;" "\n" \
1122 " -- subject uri/blank" "\n" \
1123 " DELETE FROM so_uris WHERE (OLD.s_uri_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE s_uri_id = OLD.s_uri_id)) AND (id = OLD.s_uri_id);" "\n" \
1124 " DELETE FROM so_blanks WHERE (OLD.s_blank_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE s_blank_id = OLD.s_blank_id)) AND (id = OLD.s_blank_id);" "\n" \
1125 " -- predicate uri" "\n" \
1126 " DELETE FROM p_uris WHERE (OLD.p_uri_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE p_uri_id = OLD.p_uri_id)) AND (id = OLD.p_uri_id);" "\n" \
1127 " -- object uri/blank" "\n" \
1128 " DELETE FROM so_uris WHERE (OLD.o_uri_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE o_uri_id = OLD.o_uri_id)) AND (id = OLD.o_uri_id);" "\n" \
1129 " DELETE FROM so_blanks WHERE (OLD.o_blank_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE o_blank_id = OLD.o_blank_id)) AND (id = OLD.o_blank_id);" "\n" \
1130 " -- object literal" "\n" \
1131 " DELETE FROM o_literals WHERE (OLD.o_lit_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE o_lit_id = OLD.o_lit_id)) AND (id = OLD.o_lit_id);" "\n" \
1132 " DELETE FROM t_uris WHERE (OLD.o_datatype_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM o_literals WHERE datatype_id = OLD.o_datatype_id)) AND (id = OLD.o_datatype_id);" "\n" \
1133 " -- context uri" "\n" \
1134 " DELETE FROM c_uris WHERE (OLD.c_uri_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE c_uri_id = OLD.c_uri_id)) AND (id = OLD.c_uri_id);" "\n" \
1135 "END;" "\n" \
1136 "PRAGMA user_version=2;" "\n" \
1138 // generated via tools/sql2c.sh sql/schema_mig_to_3.sql
1139 "DROP TRIGGER triples_delete;" "\n" \
1140 "CREATE TRIGGER triples_delete INSTEAD OF DELETE ON triples" "\n" \
1141 "FOR EACH ROW BEGIN" "\n" \
1142 " -- subject uri/blank" "\n" \
1143 " DELETE FROM so_uris WHERE (OLD.s_uri_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE s_uri_id = OLD.s_uri_id)) AND (id = OLD.s_uri_id);" "\n" \
1144 " DELETE FROM so_blanks WHERE (OLD.s_blank_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE s_blank_id = OLD.s_blank_id)) AND (id = OLD.s_blank_id);" "\n" \
1145 " -- predicate uri" "\n" \
1146 " DELETE FROM p_uris WHERE (OLD.p_uri_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE p_uri_id = OLD.p_uri_id)) AND (id = OLD.p_uri_id);" "\n" \
1147 " -- object uri/blank" "\n" \
1148 " DELETE FROM so_uris WHERE (OLD.o_uri_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE o_uri_id = OLD.o_uri_id)) AND (id = OLD.o_uri_id);" "\n" \
1149 " DELETE FROM so_blanks WHERE (OLD.o_blank_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE o_blank_id = OLD.o_blank_id)) AND (id = OLD.o_blank_id);" "\n" \
1150 " -- object literal" "\n" \
1151 " DELETE FROM o_literals WHERE (OLD.o_lit_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE o_lit_id = OLD.o_lit_id)) AND (id = OLD.o_lit_id);" "\n" \
1152 " DELETE FROM t_uris WHERE (OLD.o_datatype_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM o_literals WHERE datatype_id = OLD.o_datatype_id)) AND (id = OLD.o_datatype_id);" "\n" \
1153 " -- context uri" "\n" \
1154 " DELETE FROM c_uris WHERE (OLD.c_uri_id IS NOT NULL) AND (0 == (SELECT COUNT(id) FROM triple_relations WHERE c_uri_id = OLD.c_uri_id)) AND (id = OLD.c_uri_id);" "\n" \
1155 " -- triple" "\n" \
1156 " DELETE FROM triple_relations WHERE id = OLD.id;" "\n" \
1157 "END;" "\n" \
1158 "PRAGMA user_version=3;" "\n" \
1160 NULL
1163 const size_t mig_count = array_length(migrations) - 1;
1164 assert(3 == mig_count && "migrations count wrong.");
1165 assert(!migrations[mig_count] && "migrations must be NULL terminated.");
1166 if( mig_count < schema_version ) {
1167 // schema is more recent than this source file knows to handle.
1168 pub_close(storage);
1169 return RET_ERROR;
1172 const sqlite_rc_t begin = transaction_start(storage);
1173 for( int v = schema_version; migrations[v]; v++ ) {
1174 if( SQLITE_OK != ( rc = exec_stmt(db_ctx->db, migrations[v]) ) ) {
1175 transaction_rollback(storage, begin);
1176 pub_close(storage);
1177 return rc;
1180 // assert new schema version
1181 sqlite3_stmt *stmt = NULL;
1182 prep_stmt(db_ctx->db, &stmt, "PRAGMA user_version");
1183 if( SQLITE_ROW != ( rc = sqlite3_step(stmt) ) ) {
1184 sqlite3_finalize(stmt);
1185 pub_close(storage);
1186 return rc;
1188 const int v_new = sqlite3_column_int(stmt, 0);
1189 if( SQLITE_DONE != ( rc = sqlite3_step(stmt) ) ) {
1190 sqlite3_finalize(stmt);
1191 pub_close(storage);
1192 return rc;
1194 sqlite3_finalize(stmt);
1195 assert(v + 1 == v_new && "invalid schema version after migration.");
1198 if( SQLITE_OK != ( rc = transaction_commit(storage, begin) ) ) {
1199 pub_close(storage);
1200 return rc;
1203 return RET_OK;
1208 * librdf_storage_sqlite_get_feature:
1209 * @storage: #librdf_storage object
1210 * @feature: #librdf_uri feature property
1212 * Get the value of a storage feature.
1214 * Return value: #librdf_node feature value or NULL if no such feature
1215 * exists or the value is empty.
1217 static librdf_node *pub_get_feature(librdf_storage *storage, librdf_uri *feature)
1219 assert(storage && "storage must be set");
1220 if( !feature )
1221 return NULL;
1222 const char *feat = (char *)librdf_uri_as_string(feature);
1223 if( !feat )
1224 return NULL;
1225 instance_t *db_ctx = get_instance(storage);
1227 librdf_node *ret = NULL;
1228 librdf_uri *uri_xsd_boolean = librdf_new_uri(get_world(storage), (str_uri_t)"http://www.w3.org/2000/10/XMLSchema#" "boolean");
1229 librdf_uri *uri_xsd_unsignedShort = librdf_new_uri(get_world(storage), (str_uri_t)"http://www.w3.org/2000/10/XMLSchema#" "unsignedShort");
1231 if( !ret && 0 == strcmp(LIBRDF_MODEL_FEATURE_CONTEXTS, feat) )
1232 ret = librdf_new_node_from_typed_literal(get_world(storage), (str_lit_val_t)(true ? "1" : "0"), NULL, uri_xsd_boolean);
1233 if( !ret && 0 == strcmp( (char *)LIBRDF_STORAGE_SQLITE_MRO_FEATURE_SQLITE3_PROFILE, feat ) )
1234 ret = librdf_new_node_from_typed_literal(get_world(storage), (str_lit_val_t)(db_ctx->do_profile ? "true" : "false"), NULL, uri_xsd_boolean);
1235 if( !ret && 0 == strcmp( (char *)LIBRDF_STORAGE_SQLITE_MRO_FEATURE_SQLITE3_EXPLAIN_QUERY_PLAN, (char *)feat ) )
1236 ret = librdf_new_node_from_typed_literal(get_world(storage), (str_lit_val_t)(db_ctx->do_explain_query_plan ? "true" : "false"), NULL, uri_xsd_boolean);
1237 if( !ret && 0 == strcmp( (char *)LIBRDF_STORAGE_SQLITE_MRO_FEATURE_SQL_CACHE_MASK, feat ) ) {
1238 char buf[10];
1239 snprintf(buf, sizeof(buf) - 1, "%d", db_ctx->sql_cache_mask);
1240 ret = librdf_new_node_from_typed_literal(get_world(storage), (str_uri_t)buf, NULL, uri_xsd_unsignedShort);
1243 librdf_free_uri(uri_xsd_boolean);
1244 librdf_free_uri(uri_xsd_unsignedShort);
1245 return ret;
1250 * librdf_storage_set_feature:
1251 * @storage: #librdf_storage object
1252 * @feature: #librdf_uri feature property
1253 * @value: #librdf_node feature property value
1255 * Set the value of a storage feature.
1257 * Return value: non 0 on failure (negative if no such feature)
1259 static int pub_set_feature(librdf_storage *storage, librdf_uri *feature, librdf_node *value)
1261 assert(storage && "storage must be set");
1262 if( !feature )
1263 return -1;
1264 const char *feat = (char *)librdf_uri_as_string(feature);
1265 if( !feat )
1266 return -1;
1267 const char *val = (char *)librdf_node_get_literal_value(value);
1268 // librdf_log(get_world(storage), 0, LIBRDF_LOG_DEBUG, LIBRDF_FROM_STORAGE, NULL, "pub_set_feature('%s', '%s')", feat, val);
1270 instance_t *db_ctx = get_instance(storage);
1271 if( 0 == strcmp( (char *)LIBRDF_STORAGE_SQLITE_MRO_FEATURE_SQL_CACHE_MASK, feat ) ) {
1272 if( 0 == strcmp("0", val) ) {
1273 db_ctx->sql_cache_mask = 0;
1274 } else {
1275 char *end = NULL;
1276 const long i = strtol(val, &end, 10);
1277 if( NULL == end || '\0' != *end ) {
1278 librdf_log(NULL, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "invalid value: <%s> \"%s\"^^xsd:unsignedShort", feat, val);
1279 return 3;
1281 db_ctx->sql_cache_mask = ALL_PARAMS & i; // clip range
1283 // librdf_log(NULL, 0, LIBRDF_LOG_DEBUG, LIBRDF_FROM_STORAGE, NULL, "good value: <%s> \"%d\"^^xsd:unsignedShort", feat, db_ctx->sql_cache_mask);
1284 return 0;
1287 if( 0 == strcmp( (char *)LIBRDF_STORAGE_SQLITE_MRO_FEATURE_SQLITE3_PROFILE, feat ) ) {
1288 if( 0 == strcmp("1", val) || 0 == strcmp("true", val) ) {
1289 db_ctx->do_profile = true;
1290 if( db_ctx->db )
1291 sqlite3_profile(db_ctx->db, &profile, storage);
1292 } else if( 0 == strcmp("0", val) || 0 == strcmp("false", val) ) {
1293 db_ctx->do_profile = false;
1294 if( db_ctx->db )
1295 sqlite3_profile(db_ctx->db, NULL, NULL);
1296 } else {
1297 librdf_log(NULL, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "invalid value: <%s> \"%s\"^^xsd:boolean", feat, val);
1298 return 2;
1300 return 0;
1303 if( 0 == strcmp( (char *)LIBRDF_STORAGE_SQLITE_MRO_FEATURE_SQLITE3_EXPLAIN_QUERY_PLAN, feat ) ) {
1304 if( 0 == strcmp("1", val) || 0 == strcmp("true", val) )
1305 db_ctx->do_explain_query_plan = true;
1306 else if( 0 == strcmp("0", val) || 0 == strcmp("false", val) )
1307 db_ctx->do_explain_query_plan = false;
1308 else {
1309 librdf_log(NULL, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "invalid value: <%s> \"%s\"^^xsd:boolean", feat, val);
1310 return 2;
1312 return 0;
1314 return 1;
1318 #pragma mark Transactions
1321 static sqlite_rc_t pub_transaction_start(librdf_storage *storage)
1323 return transaction_start(storage);
1327 static sqlite_rc_t pub_transaction_commit(librdf_storage *storage)
1329 return transaction_commit(storage, SQLITE_OK);
1333 static sqlite_rc_t pub_transaction_rollback(librdf_storage *storage)
1335 return transaction_rollback(storage, SQLITE_OK);
1339 #pragma mark Iterator
1342 typedef struct
1344 librdf_storage *storage;
1345 librdf_statement *pattern;
1346 librdf_statement *statement;
1347 librdf_node *context;
1349 sqlite3_stmt *stmt;
1350 sqlite_rc_t txn;
1351 sqlite_rc_t rc;
1352 bool dirty;
1354 iterator_t;
1357 static int pub_iter_end_of_stream(void *_ctx)
1359 assert(_ctx && "context mustn't be NULL");
1360 iterator_t *ctx = (iterator_t *)_ctx;
1361 return SQLITE_ROW != ctx->rc;
1365 static int pub_iter_next_statement(void *_ctx)
1367 assert(_ctx && "context mustn't be NULL");
1368 iterator_t *ctx = (iterator_t *)_ctx;
1369 if( pub_iter_end_of_stream(ctx) )
1370 return RET_ERROR;
1371 ctx->dirty = true;
1372 ctx->rc = sqlite3_step(ctx->stmt);
1373 if( pub_iter_end_of_stream(ctx) )
1374 return RET_ERROR;
1375 return RET_OK;
1379 static void *pub_iter_get_statement(void *_ctx, const int _flags)
1381 assert(_ctx && "context mustn't be NULL");
1382 const librdf_iterator_get_method_flags flags = (librdf_iterator_get_method_flags)_flags;
1383 iterator_t *ctx = (iterator_t *)_ctx;
1385 switch( flags ) {
1386 case LIBRDF_ITERATOR_GET_METHOD_GET_OBJECT: {
1387 if( ctx->dirty && !pub_iter_end_of_stream(_ctx) ) {
1388 assert(ctx->statement && "statement mustn't be NULL");
1389 librdf_world *w = get_world(ctx->storage);
1390 librdf_statement *st = ctx->statement;
1391 sqlite3_stmt *stm = ctx->stmt;
1392 librdf_statement_clear(st);
1393 // stmt columns refer to find_triples_sql
1395 /* subject */
1396 librdf_node *node = NULL;
1397 const str_uri_t uri = column_uri_string(stm, IDX_S_URI);
1398 if( uri ) {
1399 assert('\0' != uri[0] && "empty uri");
1400 node = librdf_new_node_from_uri_string(w, uri);
1402 if( !node ) {
1403 const str_blank_t blank = column_blank_string(stm, IDX_S_BLANK);
1404 if( blank ) {
1405 assert('\0' != blank[0] && "empty blank");
1406 node = librdf_new_node_from_blank_identifier(w, blank);
1409 if( !node )
1410 return NULL;
1411 librdf_statement_set_subject(st, node);
1414 /* predicate */
1415 librdf_node *node = NULL;
1416 const str_uri_t uri = column_uri_string(stm, IDX_P_URI);
1417 if( uri )
1418 node = librdf_new_node_from_uri_string(w, uri);
1419 if( !node )
1420 return NULL;
1421 librdf_statement_set_predicate(st, node);
1424 /* object */
1425 librdf_node *node = NULL;
1426 const str_uri_t uri = column_uri_string(stm, IDX_O_URI);
1427 if( uri )
1428 node = librdf_new_node_from_uri_string(w, uri);
1429 if( !node ) {
1430 const str_blank_t blank = column_blank_string(stm, IDX_O_BLANK);
1431 if( blank )
1432 node = librdf_new_node_from_blank_identifier(w, blank);
1434 if( !node ) {
1435 const str_lit_val_t val = (str_lit_val_t)sqlite3_column_text(stm, IDX_O_TEXT);
1436 const str_lang_t lang = (str_lang_t)column_language(stm, IDX_O_LANGUAGE);
1437 const str_uri_t uri = column_uri_string(stm, IDX_O_DATATYPE);
1438 librdf_uri *t = uri ? librdf_new_uri(w, uri) : NULL;
1439 node = librdf_new_node_from_typed_literal(w, val, lang, t);
1440 librdf_free_uri(t);
1442 if( !node )
1443 return NULL;
1444 librdf_statement_set_object(st, node);
1446 assert(librdf_statement_is_complete(st) && "found statement must be complete");
1447 assert( ( (NULL == ctx->pattern) || librdf_statement_match(st, ctx->pattern) ) && "match candidate doesn't match." );
1448 assert(st == ctx->statement && "mismatch.");
1449 ctx->dirty = false;
1451 assert(librdf_statement_is_complete(ctx->statement) && "found statement must be complete");
1452 assert( ( (NULL == ctx->pattern) || librdf_statement_match(ctx->statement, ctx->pattern) ) && "match candidate doesn't match." );
1453 return ctx->statement;
1455 case LIBRDF_ITERATOR_GET_METHOD_GET_CONTEXT:
1456 return ctx->context;
1457 default:
1458 librdf_log(get_world(ctx->storage), 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "Unknown iterator method flag %d", flags);
1459 return NULL;
1461 return NULL;
1465 static void pub_iter_finished(void *_ctx)
1467 assert(_ctx && "context mustn't be NULL");
1468 iterator_t *ctx = (iterator_t *)_ctx;
1469 if( ctx->pattern )
1470 librdf_free_statement(ctx->pattern);
1471 if( ctx->statement )
1472 librdf_free_statement(ctx->statement);
1473 librdf_storage_remove_reference(ctx->storage);
1474 transaction_rollback(ctx->storage, ctx->txn);
1475 sqlite3_finalize(ctx->stmt);
1477 LIBRDF_FREE(iterator_t *, ctx);
1481 typedef struct
1483 librdf_storage *storage;
1484 librdf_node *context;
1486 sqlite3_stmt *stmt;
1487 sqlite_rc_t rc;
1488 bool dirty;
1490 context_iterator_t;
1493 static int context_iter_is_end(void *_ctx)
1495 assert(_ctx && "context mustn't be NULL");
1496 context_iterator_t *ctx = (context_iterator_t *)_ctx;
1497 return SQLITE_ROW != ctx->rc;
1501 static int context_iter_get_next(void *_ctx)
1503 assert(_ctx && "context mustn't be NULL");
1504 context_iterator_t *ctx = (context_iterator_t *)_ctx;
1505 if( context_iter_is_end(ctx) )
1506 return RET_ERROR;
1507 ctx->dirty = true;
1508 ctx->rc = sqlite3_step(ctx->stmt);
1509 if( context_iter_is_end(ctx) )
1510 return RET_ERROR;
1511 return RET_OK;
1515 static void *context_iter_get_context(void *_ctx, const int _flags)
1517 assert(_ctx && "context mustn't be NULL");
1518 const librdf_iterator_get_method_flags flags = (librdf_iterator_get_method_flags)_flags;
1519 context_iterator_t *ctx = (context_iterator_t *)_ctx;
1521 switch( flags ) {
1522 case LIBRDF_ITERATOR_GET_METHOD_GET_OBJECT:
1523 break;
1524 case LIBRDF_ITERATOR_GET_METHOD_GET_CONTEXT:
1525 return NULL;
1526 default:
1527 librdf_log(get_world(ctx->storage), 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "Unknown iterator method flag %d", flags);
1528 return NULL;
1531 if( ctx->dirty && !context_iter_is_end(_ctx) ) {
1532 assert(ctx->context && "context mustn't be NULL");
1533 librdf_world *w = get_world(ctx->storage);
1534 sqlite3_stmt *stm = ctx->stmt;
1535 librdf_node *node = NULL;
1536 const str_uri_t uri = column_uri_string(stm, 0);
1537 if( uri )
1538 node = librdf_new_node_from_uri_string(w, uri);
1539 if( ctx->context )
1540 librdf_free_node(ctx->context);
1542 ctx->context = node;
1543 ctx->dirty = false;
1546 return ctx->context;
1550 static void context_iter_finished(void *_ctx)
1552 assert(_ctx && "context mustn't be NULL");
1553 context_iterator_t *ctx = (context_iterator_t *)_ctx;
1554 if( ctx->context )
1555 librdf_free_node(ctx->context);
1557 librdf_storage_remove_reference(ctx->storage);
1558 sqlite3_finalize(ctx->stmt);
1560 LIBRDF_FREE(iterator_t *, ctx);
1564 #pragma mark Query & Iterate
1567 static int pub_size(librdf_storage *storage)
1569 instance_t *db_ctx = get_instance(storage);
1570 sqlite3_stmt *stmt = prep_stmt(db_ctx->db, &(db_ctx->stmt_size), "SELECT COUNT(id) FROM triple_relations");
1571 const sqlite_rc_t rc = sqlite3_step(stmt);
1572 return SQLITE_ROW == rc ? sqlite3_column_int(stmt, 0) : -1;
1576 static librdf_iterator *pub_get_contexts(librdf_storage *storage)
1578 instance_t *db_ctx = get_instance(storage);
1580 context_iterator_t *iter = LIBRDF_CALLOC(context_iterator_t*, sizeof(context_iterator_t), 1);
1581 iter->storage = storage;
1582 iter->stmt = prep_stmt(db_ctx->db, &(iter->stmt), "SELECT uri FROM c_uris");
1583 if( !iter->stmt ) {
1584 LIBRDF_FREE(context_iterator_t*, iter);
1585 return NULL;
1587 iter->rc = sqlite3_step(iter->stmt);
1588 iter->dirty = true;
1589 librdf_storage_add_reference(iter->storage);
1591 librdf_iterator *iterator = librdf_new_iterator(get_world(storage), iter, &context_iter_is_end, &context_iter_get_next, &context_iter_get_context, &context_iter_finished);
1592 if( !iterator ) {
1593 context_iter_finished(iter);
1595 return iterator;
1599 static int pub_contains_statement(librdf_storage *storage, librdf_statement *statement)
1601 return NULL != find_statement(storage, NULL, statement, false);
1605 static librdf_stream *pub_context_find_statements(librdf_storage *storage, librdf_statement *statement, librdf_node *context_node)
1607 librdf_node *s = librdf_statement_get_subject(statement);
1608 librdf_node *p = librdf_statement_get_predicate(statement);
1609 librdf_node *o = librdf_statement_get_object(statement);
1611 // build the bitmask of parameters to set (non-NULL)
1612 const int params = 0
1613 | (LIBRDF_NODE_TYPE_RESOURCE == node_type(s) ? P_S_URI : 0)
1614 | (LIBRDF_NODE_TYPE_BLANK == node_type(s) ? P_S_BLANK : 0)
1615 | (LIBRDF_NODE_TYPE_RESOURCE == node_type(p) ? P_P_URI : 0)
1616 | (LIBRDF_NODE_TYPE_RESOURCE == node_type(o) ? P_O_URI : 0)
1617 | (LIBRDF_NODE_TYPE_BLANK == node_type(o) ? P_O_BLANK : 0)
1618 | (LIBRDF_NODE_TYPE_LITERAL == node_type(o) ? P_O_TEXT : 0)
1619 | (NULL != literal_type_uri(o) ? P_O_DATATYPE : 0)
1620 | (NULL != literal_language(o) ? P_O_LANGUAGE : 0)
1621 | (context_node ? P_C_URI : 0)
1623 assert(params <= ALL_PARAMS && "params bitmask overflow");
1625 const sqlite_rc_t begin = RET_ERROR; // transaction_start(storage);
1626 instance_t *db_ctx = get_instance(storage);
1628 sqlite3_stmt *stmt = NULL;
1630 const char find_triples_sql[] = // generated via tools/sql2c.sh find_triples.sql
1631 " -- result columns must match as in enum idx_triple_column_t" "\n" \
1632 "SELECT" "\n" \
1633 " -- all *_id (hashes):" "\n" \
1634 " id" "\n" \
1635 " ,s_uri_id" "\n" \
1636 " ,s_blank_id" "\n" \
1637 " ,p_uri_id" "\n" \
1638 " ,o_uri_id" "\n" \
1639 " ,o_blank_id" "\n" \
1640 " ,o_lit_id" "\n" \
1641 " ,o_datatype_id" "\n" \
1642 " ,c_uri_id" "\n" \
1643 " -- all values:" "\n" \
1644 " ,s_uri" "\n" \
1645 " ,s_blank" "\n" \
1646 " ,p_uri" "\n" \
1647 " ,o_uri" "\n" \
1648 " ,o_blank" "\n" \
1649 " ,o_text" "\n" \
1650 " ,o_language" "\n" \
1651 " ,o_datatype" "\n" \
1652 " ,c_uri" "\n" \
1653 "FROM triples" "\n" \
1654 "WHERE 1" "\n" \
1655 " -- subject" "\n" \
1656 "AND s_uri_id = :s_uri_id" "\n" \
1657 "AND s_blank_id = :s_blank_id" "\n" \
1658 "AND p_uri_id = :p_uri_id" "\n" \
1659 " -- object" "\n" \
1660 "AND o_uri_id = :o_uri_id" "\n" \
1661 "AND o_blank_id = :o_blank_id" "\n" \
1662 "AND o_lit_id = :o_lit_id" "\n" \
1663 " -- context node" "\n" \
1664 "AND c_uri_id = :c_uri_id" "\n" \
1667 // create a SQL working copy (on stack) to fiddle with.
1668 const size_t siz = sizeof(find_triples_sql);
1669 char sql[siz];
1670 strncpy(sql, find_triples_sql, siz);
1671 // sculpt the SQL instead building it: comment out the NULL parameter terms
1672 if( 0 == (P_S_URI & params) )
1673 strncpy(strstr(sql, "AND s_uri_id"), "-- ", 3);
1674 assert('-' != sql[0] && "'AND s_uri_id' not found in find_triples.sql");
1675 if( 0 == (P_S_BLANK & params) )
1676 strncpy(strstr(sql, "AND s_blank_id"), "-- ", 3);
1677 assert('-' != sql[0] && "'AND s_blank_id' not found in find_triples.sql");
1678 if( 0 == (P_P_URI & params) )
1679 strncpy(strstr(sql, "AND p_uri_id"), "-- ", 3);
1680 assert('-' != sql[0] && "'AND p_uri_id' not found in find_triples.sql");
1681 if( 0 == (P_O_URI & params) )
1682 strncpy(strstr(sql, "AND o_uri_id"), "-- ", 3);
1683 assert('-' != sql[0] && "'AND o_uri_id' not found in find_triples.sql");
1684 if( 0 == (P_O_BLANK & params) )
1685 strncpy(strstr(sql, "AND o_blank_id"), "-- ", 3);
1686 assert('-' != sql[0] && "'AND o_blank_id' not found in find_triples.sql");
1687 if( 0 == (P_O_TEXT & params) )
1688 strncpy(strstr(sql, "AND o_lit_id"), "-- ", 3);
1689 assert('-' != sql[0] && "'AND o_lit_id' not found in find_triples.sql");
1690 if( 0 == (P_C_URI & params) )
1691 strncpy(strstr(sql, "AND c_uri_id"), "-- ", 3);
1692 assert('-' != sql[0] && "'AND c_uri_id' not found in find_triples.sql");
1694 librdf_log(librdf_storage_get_world(storage), 0, LIBRDF_LOG_INFO, LIBRDF_FROM_STORAGE, NULL, "Created SQL statement #%d", params);
1695 prep_stmt(db_ctx->db, &stmt, sql);
1696 } // else if( false ) {
1697 // toggle via "profile" feature?
1698 // librdf_log( librdf_storage_get_world(storage), 0, LIBRDF_LOG_INFO, LIBRDF_FROM_STORAGE, NULL, "%s", librdf_statement_to_string(statement) );
1699 // }
1701 const sqlite_rc_t rc = bind_stmt(db_ctx, statement, context_node, stmt);
1702 assert(SQLITE_OK == rc && "find_statements: failed to bind SQL parameters");
1704 if( db_ctx->do_explain_query_plan ) {
1705 librdf_log(librdf_storage_get_world(storage), 0, LIBRDF_LOG_INFO, LIBRDF_FROM_STORAGE, NULL, "Execute SQL statement #%d", params);
1706 printExplainQueryPlan(stmt);
1708 librdf_world *w = get_world(storage);
1709 // create iterator
1710 iterator_t *iter = LIBRDF_CALLOC(iterator_t *, sizeof(iterator_t), 1);
1711 iter->storage = storage;
1712 iter->context = context_node;
1713 iter->pattern = librdf_new_statement_from_statement(statement);
1714 iter->stmt = stmt;
1715 iter->txn = begin;
1716 iter->rc = sqlite3_step(stmt);
1717 iter->statement = librdf_new_statement(w);
1718 iter->dirty = true;
1720 librdf_storage_add_reference(iter->storage);
1721 librdf_stream *stream = librdf_new_stream(w, iter, &pub_iter_end_of_stream, &pub_iter_next_statement, &pub_iter_get_statement, &pub_iter_finished);
1723 return stream;
1727 static librdf_stream *pub_find_statements(librdf_storage *storage, librdf_statement *statement)
1729 return pub_context_find_statements(storage, statement, NULL);
1733 static librdf_stream *pub_context_serialise(librdf_storage *storage, librdf_node *context_node)
1735 return pub_context_find_statements(storage, NULL, context_node);
1739 static librdf_stream *pub_serialise(librdf_storage *storage)
1741 return pub_context_serialise(storage, NULL);
1745 #pragma mark Add
1748 static int pub_context_add_statement(librdf_storage *storage, librdf_node *context_node, librdf_statement *statement)
1750 if( !storage )
1751 return RET_ERROR;
1752 if( !statement )
1753 return RET_OK;
1754 // librdf_log( librdf_storage_get_world(storage), 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL, "%s", librdf_statement_to_string(statement) );
1755 return NULL == find_statement(storage, context_node, statement, true) ? RET_ERROR : RET_OK;
1759 static int pub_context_add_statements(librdf_storage *storage, librdf_node *context_node, librdf_stream *statement_stream)
1761 const sqlite_rc_t txn = transaction_start(storage);
1762 for( ; !librdf_stream_end(statement_stream); librdf_stream_next(statement_stream) ) {
1763 librdf_statement *stmt = librdf_stream_get_object(statement_stream);
1764 const int rc = pub_context_add_statement(storage, context_node, stmt);
1765 if( RET_OK != rc ) {
1766 transaction_rollback(storage, txn);
1767 return rc;
1770 return transaction_commit(storage, txn);
1774 static int pub_add_statement(librdf_storage *storage, librdf_statement *statement)
1776 return pub_context_add_statement(storage, NULL, statement);
1780 static int pub_add_statements(librdf_storage *storage, librdf_stream *statement_stream)
1782 return pub_context_add_statements(storage, NULL, statement_stream);
1786 #pragma mark Remove
1789 static int pub_context_remove_statement(librdf_storage *storage, librdf_node *context_node, librdf_statement *statement)
1791 if( !statement )
1792 return RET_OK;
1793 if( !librdf_statement_is_complete(statement) )
1794 return RET_ERROR;
1795 assert(storage && "must be set");
1797 instance_t *db_ctx = get_instance(storage);
1799 const hash_t stmt_id = stmt_hash(statement, context_node, db_ctx->digest);
1800 assert(!isNULL_ID(stmt_id) && "mustn't be nil");
1802 sqlite3_stmt *stmt = prep_stmt(db_ctx->db, &(db_ctx->stmt_triple_delete), "DELETE FROM triples WHERE id = :stmt_id");
1804 const sqlite_rc_t rc = bind_int(stmt, ":stmt_id", stmt_id);
1805 if( SQLITE_OK != rc )
1806 return rc;
1808 const sqlite_rc_t rc = sqlite3_step(stmt);
1809 return SQLITE_DONE == rc ? RET_OK : rc;
1813 static int pub_remove_statement(librdf_storage *storage, librdf_statement *statement)
1815 return pub_context_remove_statement(storage, NULL, statement);
1819 #if 0
1820 static int pub_context_remove_statements(librdf_storage *storage, librdf_node *context_node)
1822 const sqlite_rc_t txn = transaction_start(storage);
1823 for( librdf_statement *stmt = librdf_stream_get_object(statement_stream); !librdf_stream_end(statement_stream); librdf_stream_next(statement_stream) ) {
1824 const int rc = pub_context_remove_statement(storage, context_node, stmt);
1825 if( RET_OK != rc ) {
1826 transaction_rollback(storage, txn);
1827 return rc;
1830 return transaction_commit(storage, txn);
1834 #endif
1837 #pragma mark Register Storage Factory
1840 static void register_factory(librdf_storage_factory *factory)
1842 assert(!strcmp(factory->name, LIBRDF_STORAGE_SQLITE_MRO) && "wrong factory name");
1844 factory->version = LIBRDF_STORAGE_INTERFACE_VERSION;
1845 factory->init = pub_init;
1846 factory->terminate = pub_terminate;
1847 factory->open = pub_open;
1848 factory->close = pub_close;
1849 factory->size = pub_size;
1850 factory->add_statement = pub_add_statement;
1851 factory->add_statements = pub_add_statements;
1852 factory->remove_statement = pub_remove_statement;
1853 factory->contains_statement = pub_contains_statement;
1854 factory->serialise = pub_serialise;
1855 factory->find_statements = pub_find_statements;
1856 factory->context_add_statement = pub_context_add_statement;
1857 factory->context_add_statements = pub_context_add_statements;
1858 factory->context_remove_statement = pub_context_remove_statement;
1859 // factory->context_remove_statements = pub_context_remove_statements; is this a 'clear' ?
1860 factory->context_serialise = pub_context_serialise;
1861 factory->find_statements_in_context = pub_context_find_statements;
1862 factory->get_contexts = pub_get_contexts;
1863 factory->get_feature = pub_get_feature;
1864 factory->set_feature = pub_set_feature;
1865 factory->transaction_start = pub_transaction_start;
1866 factory->transaction_commit = pub_transaction_commit;
1867 factory->transaction_rollback = pub_transaction_rollback;
1871 int librdf_init_storage_sqlite_mro(librdf_world *world)
1873 return librdf_storage_register_factory(world, LIBRDF_STORAGE_SQLITE_MRO, "SQLite", &register_factory);