1 /* -*- Mode: c; c-basic-offset: 2 -*-
2 *
3 * rdf_storage_mysql.c - RDF Storage in MySQL DB interface definition.
4 *
5 * Based in part on rdf_storage_list and rdf_storage_parka.
6 *
7 * Copyright (C) 2003-2005 Morten Frederiksen - http://purl.org/net/morten/
8 * Copyright (C) 2000-2008, David Beckett http://www.dajobe.org/
9 * Copyright (C) 2000-2005, University of Bristol, UK http://www.bristol.ac.uk/
10 *
11 * This package is Free Software and part of Redland http://librdf.org/
12 *
13 * It is licensed under the following three licenses as alternatives:
14 * 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version
15 * 2. GNU General Public License (GPL) V2 or any newer version
16 * 3. Apache License, V2.0 or any newer version
17 *
18 * You may not use this file except in compliance with at least one of
19 * the above three licenses.
20 *
21 * See LICENSE.html or LICENSE.txt at the top of this package for the
22 * complete terms and further detail along with the license texts for
23 * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively.
24 *
25 *
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include <rdf_config.h>
30 #endif
31
32 #ifdef WIN32
33 #include <win32_rdf_config.h>
34 #include <config-win.h>
35 #include <winsock.h>
36 #include <assert.h>
37 #endif
38
39 #include <stdio.h>
40 #include <string.h>
41 #ifdef HAVE_STDLIB_H
42 #include <stdlib.h>
43 #endif
44 #include <sys/types.h>
45 #include <limits.h>
46
47 #include <redland.h>
48 #include <rdf_types.h>
49
50 #include <mysql.h>
51 #include <mysqld_error.h>
52
53
54 /* Define to emit SQL: statements to stderr */
55 /*
56 #define LIBRDF_DEBUG_SQL 1
57 */
58
59 typedef enum {
60 TABLE_RESOURCES,
61 TABLE_BNODES,
62 TABLE_LITERALS,
63 TABLE_STATEMENTS,
64 TABLE_MODELS,
65 TABLE_LAST = TABLE_MODELS
66 } mysql_table_numbers;
67
68 typedef enum {
69 TRIPLE_URI =0,
70 TRIPLE_BLANK =1,
71 TRIPLE_LITERAL=2,
72 TRIPLE_NONE =3,
73 } triple_node_type;
74
75
76 typedef struct
77 {
78 const char *name;
79 const char *schema;
80 const char *columns; /* Excluding key column, always called ID */
81 } table_info;
82
83
84 typedef struct
85 {
86 /* how many ints form the primary key for this row. e.g. for statements=4 */
87 short key_len;
88
89 u64 uints[4]; /* 4 is for Statements S,P,O,C, rest 1=ID, 2... */
90 char *strings[3]; /* 3 is for Literals longtext, text, text */
91 size_t strings_len[3];
92 int strings_count;
93 } pending_row;
94
95
96 static const table_info mysql_tables[TABLE_LAST+1]={
97 /* VALUES "(" UINT64_T_FMT ",'%s')" */
98 { "Resources",
99 "ID bigint unsigned NOT NULL, URI text NOT NULL,",
100 "URI" },
101
102 /* VALUES "(" UINT64_T_FMT ",'%s')" */
103 { "Bnodes",
104 "ID bigint unsigned NOT NULL, Name text NOT NULL,",
105 "Name" },
106
107 /* VALUES "(" UINT64_T_FMT ",'%s','%s','%s')" */
108 { "Literals",
109 "ID bigint unsigned NOT NULL, Value longtext NOT NULL, Language text NOT NULL, Datatype text NOT NULL",
110 "Value, Language, Datatype" },
111
112 /* VALUES "(" UINT64_T_FMT "," UINT64_T_FMT "," UINT64_T_FMT "," UINT64_T_FMT ")" */
113 { NULL /* Statements%d" */,
114 "",
115 "Subject, Predicate, Object, Context" },
116
117 /* VALUES "(" UINT64_T_FMT ",'%s')" */
118 { "Models",
119 "ID bigint unsigned NOT NULL, Name text NOT NULL,",
120 "Name" }
121
122 };
123
124
125 typedef enum {
126 /* Status of individual MySQL connections */
127 LIBRDF_STORAGE_MYSQL_CONNECTION_CLOSED = 0,
128 LIBRDF_STORAGE_MYSQL_CONNECTION_OPEN = 1,
129 LIBRDF_STORAGE_MYSQL_CONNECTION_BUSY = 2
130 } librdf_storage_mysql_connection_status;
131
132 typedef struct {
133 /* A MySQL connection */
134 librdf_storage_mysql_connection_status status;
135 MYSQL *handle;
136 } librdf_storage_mysql_connection;
137
138 typedef struct {
139 /* MySQL connection parameters */
140 char *host;
141 unsigned int port;
142 char *database;
143 char *user;
144 char *password;
145
146 /* Array of virtual MySQL connections */
147 librdf_storage_mysql_connection *connections;
148 int connections_count;
149
150 /* hash of model name in the database (table Models, column ID) */
151 u64 model;
152
153 /* if inserts should be optimized by locking and index optimizations */
154 int bulk;
155
156 /* if a table with merged models should be maintained */
157 int merge;
158
159 /* if mysql MYSQL_OPT_RECONNECT should be set on new connections */
160 int reconnect;
161
162 /* digest object for node hashes */
163 librdf_digest *digest;
164
165 MYSQL* transaction_handle;
166
167 raptor_sequence* pending_inserts[4];
168 librdf_hash* pending_insert_hash_nodes;
169 raptor_sequence* pending_statements;
170
171 /* SQL config */
172 librdf_sql_config* config;
173
174 /* configuration variables */
175 librdf_hash* vars;
176
177 /* SQL schema layout - default is "v1" */
178 char *layout;
179
180 /* SQL config directory - default is defined in
181 * librdf_new_sql_config_for_storage
182 */
183 char *config_dir;
184 } librdf_storage_mysql_instance;
185
186 /* prototypes for local functions */
187 static int librdf_storage_mysql_init(librdf_storage* storage, const char *name,
188 librdf_hash* options);
189 static int librdf_storage_mysql_merge(librdf_storage* storage);
190 static void librdf_storage_mysql_terminate(librdf_storage* storage);
191 static int librdf_storage_mysql_open(librdf_storage* storage,
192 librdf_model* model);
193 static int librdf_storage_mysql_close(librdf_storage* storage);
194 static int librdf_storage_mysql_sync(librdf_storage* storage);
195 static int librdf_storage_mysql_size(librdf_storage* storage);
196 static int librdf_storage_mysql_add_statement(librdf_storage* storage,
197 librdf_statement* statement);
198 static int librdf_storage_mysql_add_statements(librdf_storage* storage,
199 librdf_stream* statement_stream);
200 static int librdf_storage_mysql_remove_statement(librdf_storage* storage,
201 librdf_statement* statement);
202 static int librdf_storage_mysql_contains_statement(librdf_storage* storage,
203 librdf_statement* statement);
204 static librdf_stream*
205 librdf_storage_mysql_serialise(librdf_storage* storage);
206 static librdf_stream*
207 librdf_storage_mysql_find_statements(librdf_storage* storage,
208 librdf_statement* statement);
209 static librdf_stream*
210 librdf_storage_mysql_find_statements_with_options(librdf_storage* storage,
211 librdf_statement* statement,
212 librdf_node* context_node,
213 librdf_hash* options);
214
215 /* context functions */
216 static int librdf_storage_mysql_context_add_statement(librdf_storage* storage,
217 librdf_node* context_node,
218 librdf_statement* statement);
219 static int librdf_storage_mysql_context_add_statements(librdf_storage* storage,
220 librdf_node* context_node,
221 librdf_stream* statement_stream);
222 static int librdf_storage_mysql_context_remove_statement(librdf_storage* storage,
223 librdf_node* context_node,
224 librdf_statement* statement);
225 static int librdf_storage_mysql_context_remove_statements(librdf_storage* storage,
226 librdf_node* context_node);
227 static librdf_stream*
228 librdf_storage_mysql_context_serialise(librdf_storage* storage,
229 librdf_node* context_node);
230 static librdf_stream* librdf_storage_mysql_find_statements_in_context(librdf_storage* storage,
231 librdf_statement* statement,
232 librdf_node* context_node);
233 static librdf_iterator* librdf_storage_mysql_get_contexts(librdf_storage* storage);
234
235 /* "private" helper definitions */
236 typedef struct {
237 librdf_storage *storage;
238 librdf_statement *current_statement;
239 librdf_node *current_context;
240 librdf_statement *query_statement;
241 librdf_node *query_context;
242 MYSQL *handle;
243 MYSQL_RES *results;
244 int is_literal_match;
245 } librdf_storage_mysql_sos_context;
246
247 typedef struct {
248 librdf_storage *storage;
249 librdf_node *current_context;
250 MYSQL *handle;
251 MYSQL_RES *results;
252 } librdf_storage_mysql_get_contexts_context;
253
254 static u64 librdf_storage_mysql_hash(librdf_storage* storage, const char *type,
255 const char *string, size_t length);
256
257 #define NODE_HASH_MODE_GET_HASH 0
258 #define NODE_HASH_MODE_STORE_NODE 1
259 static u64 librdf_storage_mysql_node_hash_common(librdf_storage* storage,
260 librdf_node* node,
261 int mode);
262 static u64 librdf_storage_mysql_get_node_hash(librdf_storage* storage, librdf_node* node);
263 static u64 librdf_storage_mysql_store_node(librdf_storage* storage, librdf_node* node);
264 static int librdf_storage_mysql_start_bulk(librdf_storage* storage);
265 static int librdf_storage_mysql_stop_bulk(librdf_storage* storage);
266 static int librdf_storage_mysql_context_add_statement_helper(librdf_storage* storage,
267 u64 ctxt,
268 librdf_statement* statement);
269 static int librdf_storage_mysql_find_statements_in_context_augment_query(char **query, const char *addition);
270
271 /* methods for stream of statements */
272 static int librdf_storage_mysql_find_statements_in_context_end_of_stream(void* context);
273 static int librdf_storage_mysql_find_statements_in_context_next_statement(void* context);
274 static void* librdf_storage_mysql_find_statements_in_context_get_statement(void* context, int flags);
275 static void librdf_storage_mysql_find_statements_in_context_finished(void* context);
276
277 /* methods for iterator for contexts */
278 static int librdf_storage_mysql_get_contexts_end_of_iterator(void* context);
279 static int librdf_storage_mysql_get_contexts_next_context(void* context);
280 static void* librdf_storage_mysql_get_contexts_get_context(void* context, int flags);
281 static void librdf_storage_mysql_get_contexts_finished(void* context);
282
283 static int librdf_storage_mysql_transaction_rollback(librdf_storage* storage);
284
285 static void librdf_storage_mysql_register_factory(librdf_storage_factory *factory);
286 #ifdef MODULAR_LIBRDF
287 void librdf_storage_module_register_factory(librdf_world *world);
288 #endif
289
290
291 /* functions implementing storage api */
292
293 /*
294 * librdf_storage_mysql_hash - Find hash value of string.
295 * @storage: the storage
296 * @type: character type of node to hash ("R", "L" or "B")
297 * @string: a string to get hash for
298 * @length: length of string
299 *
300 * Find hash value of string.
301 *
302 * Return value: Non-zero on succes.
303 **/
304 static u64
librdf_storage_mysql_hash(librdf_storage * storage,const char * type,const char * string,size_t length)305 librdf_storage_mysql_hash(librdf_storage* storage, const char *type,
306 const char *string, size_t length)
307 {
308 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
309 u64 hash;
310 byte* digest;
311 uint i;
312
313 /* (Re)initialize digest object */
314 librdf_digest_init(context->digest);
315
316 /* Update digest with data */
317 if(type)
318 librdf_digest_update(context->digest, (unsigned char*)type, 1);
319 librdf_digest_update(context->digest, (unsigned char*)string, length);
320 librdf_digest_final(context->digest);
321
322 /* Copy first 8 bytes of digest into unsigned 64bit hash
323 * using a method portable across big/little endianness
324 *
325 * Fixes Issue#0000023 - http://bugs.librdf.org/mantis/view.php?id=23
326 */
327 digest = (byte*) librdf_digest_get_digest(context->digest);
328 hash = 0;
329 for(i=0; i<8; i++)
330 hash += ((u64) digest[i]) << (i*8);
331
332 return hash;
333 }
334
335
336 /*
337 * librdf_storage_mysql_init_connections - Initialize MySQL connection pool.
338 * @storage: the storage
339 *
340 * Return value: Non-zero on success.
341 **/
342 static int
librdf_storage_mysql_init_connections(librdf_storage * storage)343 librdf_storage_mysql_init_connections(librdf_storage* storage)
344 {
345 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
346
347 /* Reset connection pool */
348 context->connections=NULL;
349 context->connections_count=0;
350 return 0;
351 }
352
353
354 /*
355 * librdf_storage_mysql_finish_connections - Finish all connections in MySQL connection pool and free structures.
356 * @storage: the storage
357 *
358 * Return value: None.
359 **/
360 static void
librdf_storage_mysql_finish_connections(librdf_storage * storage)361 librdf_storage_mysql_finish_connections(librdf_storage* storage)
362 {
363 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
364 int i;
365
366 /* Loop through connections and close */
367 for(i=0; i < context->connections_count; i++) {
368 if(LIBRDF_STORAGE_MYSQL_CONNECTION_CLOSED != context->connections[i].status)
369 #ifdef LIBRDF_DEBUG_SQL
370 LIBRDF_DEBUG2("mysql_close connection handle %p\n",
371 context->connections[i].handle);
372 #endif
373 mysql_close(context->connections[i].handle);
374 }
375 /* Free structure and reset */
376 if (context->connections_count) {
377 LIBRDF_FREE(librdf_storage_mysql_connection*, context->connections);
378 context->connections=NULL;
379 context->connections_count=0;
380 }
381 }
382
383 /*
384 * librdf_storage_mysql_get_handle - get a connection handle to the MySQL server
385 * @storage: the storage
386 *
387 * This attempts to reuses any existing available pooled connection
388 * otherwise creates a new connection to the server.
389 *
390 * Return value: Non-zero on succes.
391 **/
392 static MYSQL*
librdf_storage_mysql_get_handle(librdf_storage * storage)393 librdf_storage_mysql_get_handle(librdf_storage* storage)
394 {
395 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
396 librdf_storage_mysql_connection* connection= NULL;
397 int i;
398
399 if(context->transaction_handle)
400 return context->transaction_handle;
401
402 /* Look for an open connection handle to return */
403 for(i=0; i < context->connections_count; i++) {
404 if(LIBRDF_STORAGE_MYSQL_CONNECTION_OPEN == context->connections[i].status) {
405 context->connections[i].status=LIBRDF_STORAGE_MYSQL_CONNECTION_BUSY;
406 return context->connections[i].handle;
407 }
408 }
409
410 /* Look for a closed connection */
411 for(i=0; i < context->connections_count && !connection; i++) {
412 if(LIBRDF_STORAGE_MYSQL_CONNECTION_CLOSED == context->connections[i].status) {
413 connection=&context->connections[i];
414 break;
415 }
416 }
417 /* Expand connection pool if no closed connection was found */
418 if (!connection) {
419 /* Allocate new buffer with two extra slots */
420 librdf_storage_mysql_connection* connections;
421 connections = LIBRDF_CALLOC(librdf_storage_mysql_connection*,
422 LIBRDF_GOOD_CAST(size_t, context->connections_count + 2),
423 sizeof(librdf_storage_mysql_connection));
424 if(!connections)
425 return NULL;
426
427 if (context->connections_count) {
428 /* Copy old buffer to new */
429 memcpy(connections, context->connections,
430 sizeof(librdf_storage_mysql_connection) * LIBRDF_GOOD_CAST(size_t, context->connections_count));
431 /* Free old buffer */
432 LIBRDF_FREE(librdf_storage_mysql_connection*, context->connections);
433 }
434
435 /* Update buffer size and reset new connections */
436 context->connections_count+=2;
437 connection=&connections[context->connections_count-2];
438 connection->status=LIBRDF_STORAGE_MYSQL_CONNECTION_CLOSED;
439 connection->handle=NULL;
440 connections[context->connections_count-1].status=LIBRDF_STORAGE_MYSQL_CONNECTION_CLOSED;
441 connections[context->connections_count-1].handle=NULL;
442 context->connections=connections;
443 }
444
445 /* Initialize closed MySQL connection handle */
446 connection->handle=mysql_init(connection->handle);
447
448 #ifdef HAVE_MYSQL_OPT_RECONNECT
449 if(1) {
450 my_bool value=(context->reconnect) ? 1 : 0;
451 mysql_options(connection->handle, MYSQL_OPT_RECONNECT, &value);
452 }
453 #endif
454
455 /* Create connection to database for handle */
456 if(!mysql_real_connect(connection->handle,
457 context->host, context->user, context->password,
458 context->database, context->port, NULL, 0)) {
459 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
460 "Connection to MySQL database %s:%d name %s as user %s failed: %s",
461 context->host, context->port, context->database,
462 context->user, mysql_error(connection->handle));
463 return NULL;
464 }
465 /* Update status and return */
466 connection->status=LIBRDF_STORAGE_MYSQL_CONNECTION_BUSY;
467 return connection->handle;
468 }
469
470
471 /*
472 * librdf_storage_mysql_release_handle - Release a connection handle to MySQL server back to the pool
473 * @storage: the storage
474 * @handle: the MySQL handle to release
475 *
476 * Return value: None.
477 **/
478 static void
librdf_storage_mysql_release_handle(librdf_storage * storage,MYSQL * handle)479 librdf_storage_mysql_release_handle(librdf_storage* storage, MYSQL *handle)
480 {
481 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
482 int i;
483
484 if(handle == context->transaction_handle)
485 return;
486
487 /* Look for busy connection handle to drop */
488 for(i=0; i < context->connections_count; i++) {
489 if(LIBRDF_STORAGE_MYSQL_CONNECTION_BUSY == context->connections[i].status &&
490 context->connections[i].handle == handle) {
491 context->connections[i].status=LIBRDF_STORAGE_MYSQL_CONNECTION_OPEN;
492 return;
493 }
494 }
495 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
496 "Unable to find busy connection (in pool of %i connections) to drop for MySQL server thread: %lu",
497 context->connections_count, mysql_thread_id(handle));
498 }
499
500
501 /**
502 * librdf_storage_mysql_init:
503 * @storage: the storage
504 * @name: model name
505 * @options: host, port, database, user, password [, new] [, bulk] [, merge].
506 *
507 * .
508 *
509 * Create connection to database. Defaults to port 3306 if not given.
510 *
511 * The boolean bulk option can be set to true if optimized inserts (table
512 * locks and temporary key disabling) is wanted. Note that this will block
513 * all other access, and requires table locking and alter table privileges.
514 *
515 * The boolean merge option can be set to true if a merged "view" of all
516 * models should be maintained. This "view" will be a table with TYPE=MERGE.
517 *
518 * Return value: Non-zero on failure.
519 **/
520 static int
librdf_storage_mysql_init(librdf_storage * storage,const char * name,librdf_hash * options)521 librdf_storage_mysql_init(librdf_storage* storage, const char *name,
522 librdf_hash* options)
523 {
524 librdf_storage_mysql_instance* context;
525 const char create_model[]="INSERT INTO Models (ID,Name) VALUES (" UINT64_T_FMT ",'%s')";
526 const char check_model[]="SELECT 1 FROM Models WHERE ID=" UINT64_T_FMT " AND Name='%s'";
527 int status=0;
528 char *escaped_name=NULL;
529 char *query=NULL;
530 MYSQL_RES *res;
531 MYSQL *handle;
532 const char* default_layout="v1";
533 long lport;
534
535 /* Must have connection parameters passed as options */
536 if(!options)
537 return 1;
538
539 context = LIBRDF_CALLOC(librdf_storage_mysql_instance*, 1,
540 sizeof(librdf_storage_mysql_instance));
541
542 if(!context) {
543 librdf_free_hash(options);
544 return 1;
545 }
546 librdf_storage_set_instance(storage, context);
547
548 /* Create digest */
549 if(!(context->digest = librdf_new_digest(storage->world,"MD5"))) {
550 librdf_free_hash(options);
551 return 1;
552 }
553
554 /* Save hash of model name */
555 context->model = librdf_storage_mysql_hash(storage, NULL, (char*)name,
556 strlen(name));
557
558 /* Save connection parameters */
559 context->host = librdf_hash_get_del(options, "host");
560 if(!context->host) {
561 context->host = LIBRDF_MALLOC(char*, 10);
562 strcpy(context->host, "localhost");
563 }
564
565 lport = librdf_hash_get_as_long(options, "port");
566 if(lport < 0 || lport > INT_MAX)
567 context->port = 3306; /* default mysql port */
568 else
569 context->port = LIBRDF_GOOD_CAST(unsigned int, lport);
570
571 context->database = librdf_hash_get_del(options, "database");
572 context->user = librdf_hash_get_del(options, "user");
573 context->password = librdf_hash_get_del(options, "password");
574
575 if(!context->host || !context->database || !context->user ||
576 !context->port || !context->password) {
577 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
578 "%s storage requires database, user and password in options",
579 storage->factory->name);
580 librdf_free_hash(options);
581 return 1;
582 }
583
584 /* Maintain merge table? */
585 context->merge = (librdf_hash_get_as_boolean(options, "merge")>0);
586
587 /* Reconnect? */
588 context->reconnect = (librdf_hash_get_as_boolean(options, "reconnect")>0);
589
590 context->layout = librdf_hash_get_del(options, "layout");
591 if(!context->layout) {
592 context->layout = LIBRDF_MALLOC(char*, strlen(default_layout) + 1);
593 strcpy(context->layout, default_layout);
594 }
595
596 context->config_dir = librdf_hash_get_del(options, "config-dir");
597
598 /* Initialize MySQL connections */
599 librdf_storage_mysql_init_connections(storage);
600
601 /* Get MySQL connection handle */
602 handle = librdf_storage_mysql_get_handle(storage);
603 if(!handle) {
604 librdf_free_hash(options);
605 return 1;
606 }
607
608 /* Read SQL configuration */
609 context->config = librdf_new_sql_config_for_storage(storage, context->layout,
610 context->config_dir);
611 if(!context->config)
612 status = 1;
613
614 if(!status) {
615 char vars_str[50];
616 context->vars = librdf_new_hash(storage->world, NULL);
617
618 sprintf(vars_str, "STATEMENTS_NAME='Statements" UINT64_T_FMT "'",
619 context->model);
620 librdf_hash_from_string(context->vars, vars_str);
621 }
622
623
624 /* Create tables, if new and not existing */
625 if(!status && (librdf_hash_get_as_boolean(options, "new")>0)) {
626 int table;
627
628 for(table= DBCONFIG_CREATE_TABLE_STATEMENTS;
629 table <= DBCONFIG_CREATE_TABLE_MODELS;
630 table++) {
631 query = (char*)librdf_hash_interpret_template((const unsigned char*)context->config->values[table],
632 context->vars,
633 (const unsigned char*)"$(",
634 (const unsigned char*)")");
635
636 #ifdef LIBRDF_DEBUG_SQL
637 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
638 #endif
639 if(mysql_real_query(handle, (const char*)query,
640 strlen((const char*)query))) {
641 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE,
642 NULL,
643 "MySQL table creation failed: %s",
644 mysql_error(handle));
645 status = -1;
646 break;
647 }
648
649 LIBRDF_FREE(char*, query);
650 } /* end for */
651
652 }
653
654 /* Create model if new and not existing, or check for existence */
655 if(!status) {
656 escaped_name = LIBRDF_MALLOC(char*, strlen(name) * 2 + 1);
657 if(!escaped_name)
658 status = 1;
659 mysql_real_escape_string(handle, escaped_name,
660 (const char*)name, strlen(name));
661 }
662 if(!status && (librdf_hash_get_as_boolean(options, "new")>0)) {
663 /* Create new model */
664 query = LIBRDF_MALLOC(char*,strlen(create_model) + 20 +
665 strlen(escaped_name)+1);
666 if(!query)
667 status = 1;
668 sprintf(query, create_model, context->model, escaped_name);
669
670 #ifdef LIBRDF_DEBUG_SQL
671 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
672 #endif
673 if(!status && mysql_real_query(handle, query, strlen(query)) &&
674 mysql_errno(handle) != ER_DUP_ENTRY) {
675 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
676 "MySQL insert into Models table failed: %s",
677 mysql_error(handle));
678 status = -1;
679 }
680 /* Maintain merge table? */
681 if(!status && context->merge)
682 status = librdf_storage_mysql_merge(storage);
683 } else if(!status) {
684 /* Check for model existence */
685 query = LIBRDF_MALLOC(char*,
686 strlen(check_model) + 20 + strlen(escaped_name)+1);
687 if(!query)
688 status = 1;
689 sprintf(query, check_model, context->model, name);
690 res = NULL;
691
692 #ifdef LIBRDF_DEBUG_SQL
693 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
694 #endif
695 if(!status && (mysql_real_query(handle, query, strlen(query)) ||
696 !(res=mysql_store_result(handle)))) {
697 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
698 "MySQL select from Models table failed: %s",
699 mysql_error(handle));
700 status = -1;
701 }
702 if(!status && !(mysql_fetch_row(res))) {
703 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
704 "Unknown model: %s",name);
705 status = 1;
706 }
707 if(res)
708 mysql_free_result(res);
709 }
710 if(query)
711 LIBRDF_FREE(char*, query);
712 if(escaped_name)
713 LIBRDF_FREE(char*, escaped_name);
714
715 /* Optimize loads? */
716 context->bulk = (librdf_hash_get_as_boolean(options, "bulk")>0);
717
718 /* Truncate model? */
719 if(!status && (librdf_hash_get_as_boolean(options, "new")>0))
720 status = librdf_storage_mysql_context_remove_statements(storage, NULL);
721
722 /* Unused options: write (always...) */
723 librdf_free_hash(options);
724
725 librdf_storage_mysql_release_handle(storage, handle);
726
727 return status;
728 }
729
730 /*
731 * librdf_storage_mysql_merge - (re)create merged "view" of all models
732 * @storage: the storage
733 *
734 * Return value: Non-zero on failure.
735 */
736 static int
librdf_storage_mysql_merge(librdf_storage * storage)737 librdf_storage_mysql_merge(librdf_storage* storage)
738 {
739 const char get_models[]="SELECT ID FROM Models";
740 const char drop_table_statements[]="DROP TABLE IF EXISTS Statements";
741 const char create_table_statements[]="\
742 CREATE TABLE Statements (\
743 Subject bigint unsigned NOT NULL,\
744 Predicate bigint unsigned NOT NULL,\
745 Object bigint unsigned NOT NULL,\
746 Context bigint unsigned NOT NULL,\
747 KEY Context (Context),\
748 KEY SubjectPredicate (Subject,Predicate),\
749 KEY PredicateObject (Predicate,Object),\
750 KEY ObjectSubject (Object,Subject)\
751 ) TYPE=MERGE INSERT_METHOD=NO UNION=(";
752 char *query=NULL;
753 MYSQL_RES *res;
754 MYSQL_ROW row;
755 MYSQL *handle;
756
757 /* Get MySQL connection handle */
758 handle=librdf_storage_mysql_get_handle(storage);
759 if(!handle)
760 return 1;
761
762 /* Query for list of models. */
763 #ifdef LIBRDF_DEBUG_SQL
764 LIBRDF_DEBUG2("SQL: >>%s<<\n", get_models);
765 #endif
766 if(mysql_real_query(handle, get_models, strlen(get_models)) ||
767 !(res=mysql_store_result(handle))) {
768 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
769 "MySQL query for model list failed: %s",
770 mysql_error(handle));
771 librdf_storage_mysql_release_handle(storage, handle);
772 return -1;
773 }
774 /* Allocate space for merge table generation query. */
775 query = LIBRDF_MALLOC(char*, strlen(create_table_statements) +
776 mysql_num_rows(res)*31+2);
777 if(!query) {
778 librdf_storage_mysql_release_handle(storage, handle);
779 return 1;
780 }
781 /* Generate CSV list of models. */
782 strcpy(query,create_table_statements);
783 while((row=mysql_fetch_row(res))) {
784 strcat(query,"Statements");
785 strcat(query,row[0]);
786 strcat(query,",");
787 }
788 mysql_free_result(res);
789 query[strlen(query)-1]=')';
790
791 /* Drop and create merge table. */
792 #ifdef LIBRDF_DEBUG_SQL
793 LIBRDF_DEBUG2("SQL: >>%s<<\n", drop_table_statements);
794 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
795 #endif
796 if(mysql_real_query(handle, drop_table_statements,
797 strlen(drop_table_statements)) ||
798 mysql_real_query(handle, query, strlen(query))) {
799 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
800 "MySQL merge table creation failed: %s",
801 mysql_error(handle));
802 LIBRDF_FREE(char*, query);
803 librdf_storage_mysql_release_handle(storage, handle);
804 return -1;
805 }
806 LIBRDF_FREE(char*, query);
807 librdf_storage_mysql_release_handle(storage, handle);
808
809 return 0;
810 }
811
812 /**
813 * librdf_storage_mysql_terminate:
814 * @storage: the storage
815 *
816 * .
817 *
818 * Close the storage and database connections.
819 *
820 * Return value: None.
821 **/
822 static void
librdf_storage_mysql_terminate(librdf_storage * storage)823 librdf_storage_mysql_terminate(librdf_storage* storage)
824 {
825 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
826
827 if (context == NULL)
828 return;
829
830 librdf_storage_mysql_finish_connections(storage);
831
832 if(context->config_dir)
833 LIBRDF_FREE(char*, context->config_dir);
834
835 if(context->layout)
836 LIBRDF_FREE(char*, context->layout);
837
838 if(context->vars)
839 librdf_free_hash(context->vars);
840
841 if(context->config)
842 librdf_free_sql_config(context->config);
843
844 if(context->password)
845 LIBRDF_FREE(char*, context->password);
846
847 if(context->user)
848 LIBRDF_FREE(char*, context->user);
849
850 if(context->database)
851 LIBRDF_FREE(char*, context->database);
852
853 if(context->host)
854 LIBRDF_FREE(char*, context->host);
855
856 if(context->digest)
857 librdf_free_digest(context->digest);
858
859 if(context->transaction_handle)
860 librdf_storage_mysql_transaction_rollback(storage);
861
862 LIBRDF_FREE(librdf_storage_mysql_instance, storage->instance);
863 }
864
865 /**
866 * librdf_storage_mysql_open:
867 * @storage: the storage
868 * @model: the model
869 *
870 * .
871 *
872 * Create or open model in database (nop).
873 *
874 * Return value: Non-zero on failure.
875 **/
876 static int
librdf_storage_mysql_open(librdf_storage * storage,librdf_model * model)877 librdf_storage_mysql_open(librdf_storage* storage, librdf_model* model)
878 {
879 return 0;
880 }
881
882 /**
883 * librdf_storage_mysql_close:
884 * @storage: the storage
885 *
886 * .
887 *
888 * Close model (nop).
889 *
890 * Return value: Non-zero on failure.
891 **/
892 static int
librdf_storage_mysql_close(librdf_storage * storage)893 librdf_storage_mysql_close(librdf_storage* storage)
894 {
895 librdf_storage_mysql_transaction_rollback(storage);
896
897 return librdf_storage_mysql_sync(storage);
898 }
899
900 /**
901 * librdf_storage_mysql_sync:
902 * @storage: the storage
903 *
904 * Flush all tables, making sure they are saved on disk.
905 *
906 * Return value: Non-zero on failure.
907 **/
908 static int
librdf_storage_mysql_sync(librdf_storage * storage)909 librdf_storage_mysql_sync(librdf_storage* storage)
910 {
911 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
912
913 /* Make sure optimizing for bulk operations is stopped? */
914 if(context->bulk)
915 librdf_storage_mysql_stop_bulk(storage);
916
917 return 0;
918 }
919
920 /**
921 * librdf_storage_mysql_size:
922 * @storage: the storage
923 *
924 * .
925 *
926 * Close model (nop).
927 *
928 * Return value: Negative on failure.
929 **/
930 static int
librdf_storage_mysql_size(librdf_storage * storage)931 librdf_storage_mysql_size(librdf_storage* storage)
932 {
933 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
934 char model_size[]="SELECT COUNT(*) FROM Statements" UINT64_T_FMT;
935 char *query;
936 MYSQL_RES *res;
937 MYSQL_ROW row;
938 int count;
939 MYSQL *handle;
940
941 /* Get MySQL connection handle */
942 handle=librdf_storage_mysql_get_handle(storage);
943 if(!handle)
944 return -1;
945
946 /* Query for number of statements */
947 query = LIBRDF_MALLOC(char*, strlen(model_size) + 21);
948 if(!query) {
949 librdf_storage_mysql_release_handle(storage, handle);
950 return -1;
951 }
952 sprintf(query, model_size, context->model);
953
954 #ifdef LIBRDF_DEBUG_SQL
955 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
956 #endif
957 if(mysql_real_query(handle, query, strlen(query)) ||
958 !(res=mysql_store_result(handle)) ||
959 !(row=mysql_fetch_row(res))) {
960 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
961 "MySQL query for model size failed: %s",
962 mysql_error(handle));
963 LIBRDF_FREE(char*, query);
964 librdf_storage_mysql_release_handle(storage, handle);
965 return -1;
966 }
967 count=atol(row[0]);
968 mysql_free_result(res);
969 LIBRDF_FREE(char*, query);
970 librdf_storage_mysql_release_handle(storage, handle);
971
972 return count;
973 }
974
975
976 static int
librdf_storage_mysql_add_statement(librdf_storage * storage,librdf_statement * statement)977 librdf_storage_mysql_add_statement(librdf_storage* storage,
978 librdf_statement* statement)
979 {
980 /* Do not add duplicate statements */
981 if(librdf_storage_mysql_contains_statement(storage, statement))
982 return 0;
983
984 return librdf_storage_mysql_context_add_statement_helper(storage, 0,
985 statement);
986 }
987
988
989 /**
990 * librdf_storage_mysql_add_statements:
991 * @storage: the storage
992 * @statement_stream: the stream of statements
993 *
994 * .
995 *
996 * Add statements in stream to storage, without context.
997 *
998 * Return value: Non-zero on failure.
999 **/
1000 static int
librdf_storage_mysql_add_statements(librdf_storage * storage,librdf_stream * statement_stream)1001 librdf_storage_mysql_add_statements(librdf_storage* storage,
1002 librdf_stream* statement_stream)
1003 {
1004 int helper=0;
1005
1006 while(!helper && !librdf_stream_end(statement_stream)) {
1007 librdf_statement* statement=librdf_stream_get_object(statement_stream);
1008 /* Do not add duplicate statements */
1009 if(!librdf_storage_mysql_contains_statement(storage, statement))
1010 helper=librdf_storage_mysql_context_add_statement_helper(storage, 0,
1011 statement);
1012 librdf_stream_next(statement_stream);
1013 }
1014
1015 return helper;
1016 }
1017
1018
1019 static int
compare_pending_rows(const void * a,const void * b)1020 compare_pending_rows(const void *a, const void *b)
1021 {
1022 pending_row* prow_a=*(pending_row**)a;
1023 pending_row* prow_b=*(pending_row**)b;
1024 int i;
1025
1026 for(i=0; i< prow_a->key_len; i++) {
1027 /* These are u64 <> u64 compares - DO NOT USE 'int' here */
1028 if(prow_b->uints[i] > prow_a->uints[i])
1029 return -1;
1030 else if(prow_b->uints[i] < prow_a->uints[i])
1031 return 1;
1032 }
1033 return 0;
1034 }
1035
1036
1037
1038 static void
free_pending_row(pending_row * prow)1039 free_pending_row(pending_row* prow)
1040 {
1041 int i;
1042
1043 for(i=0; i < prow->strings_count; i++)
1044 LIBRDF_FREE(char*, prow->strings[i]);
1045
1046 LIBRDF_FREE(pending_row, prow);
1047 }
1048
1049
1050 static raptor_stringbuffer*
format_pending_row_sequence(const table_info * table,raptor_sequence * seq)1051 format_pending_row_sequence(const table_info *table, raptor_sequence* seq)
1052 {
1053 int i;
1054 raptor_stringbuffer* sb;
1055
1056 if(!raptor_sequence_size(seq))
1057 return NULL;
1058
1059 #ifdef LIBRDF_DEBUG_SQL
1060 LIBRDF_DEBUG3("Format pending row for table %s with %d entries\n",
1061 table->name, raptor_sequence_size(seq));
1062 #endif
1063
1064 sb=raptor_new_stringbuffer();
1065
1066 raptor_stringbuffer_append_string(sb,
1067 (const unsigned char*)"REPLACE INTO ", 1);
1068 raptor_stringbuffer_append_string(sb,
1069 (const unsigned char*)table->name, 1);
1070 raptor_stringbuffer_append_string(sb,
1071 (const unsigned char*)" (ID, ", 1);
1072 raptor_stringbuffer_append_string(sb,
1073 (const unsigned char*)table->columns, 1);
1074 raptor_stringbuffer_append_counted_string(sb,
1075 (const unsigned char*)") VALUES ", 9, 1);
1076
1077 for(i=0; i< raptor_sequence_size(seq); i++) {
1078 pending_row* prow;
1079 char uint64_buffer[64];
1080 int j;
1081
1082 if(i > 0)
1083 raptor_stringbuffer_append_counted_string(sb,
1084 (const unsigned char*)", ", 2, 1);
1085
1086 prow=(pending_row*)raptor_sequence_get_at(seq, i);
1087
1088 raptor_stringbuffer_append_counted_string(sb,
1089 (const unsigned char*)"(", 1, 1);
1090 /* First column is always the ID */
1091 sprintf(uint64_buffer, UINT64_T_FMT, prow->uints[0]);
1092 raptor_stringbuffer_append_string(sb,
1093 (const unsigned char*)uint64_buffer, 1);
1094
1095 for(j=0; j < prow->strings_count; j++) {
1096 raptor_stringbuffer_append_counted_string(sb,
1097 (const unsigned char*)", '", 3, 1);
1098 raptor_stringbuffer_append_string(sb,
1099 (const unsigned char*)prow->strings[j], 1);
1100 raptor_stringbuffer_append_counted_string(sb,
1101 (const unsigned char*)"'", 1, 1);
1102 }
1103
1104 raptor_stringbuffer_append_counted_string(sb,
1105 (const unsigned char*)")", 1, 1);
1106 }
1107
1108 #ifdef LIBRDF_DEBUG_SQL
1109 LIBRDF_DEBUG3("Format pending row for table %s returning query size %d\n",
1110 table->name, raptor_stringbuffer_length(sb));
1111 #endif
1112
1113 return sb;
1114 }
1115
1116
1117 /*
1118 * librdf_storage_mysql_node_hash_common - Create/get hash value for node
1119 * @storage: the storage
1120 * @node: a node to get hash for (and possibly create in database)
1121 * @mode: mode to do: 0=just do hash, 1=add them
1122 *
1123 * Return value: Non-zero on succes.
1124 **/
1125 static u64
librdf_storage_mysql_node_hash_common(librdf_storage * storage,librdf_node * node,int mode)1126 librdf_storage_mysql_node_hash_common(librdf_storage* storage,
1127 librdf_node* node,
1128 int mode)
1129 {
1130 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance* )storage->instance;
1131 librdf_node_type type=librdf_node_get_type(node);
1132 u64 hash;
1133 size_t nodelen;
1134 char *query;
1135 triple_node_type node_type;
1136 const table_info *table;
1137 MYSQL *handle;
1138 unsigned char *uri=NULL;
1139 unsigned char *value=NULL, *datatype=NULL;
1140 char *lang=NULL, *nodestring;
1141 librdf_uri *dt;
1142 size_t valuelen, langlen=0, datatypelen=0;
1143 unsigned char *name=NULL;
1144 /* Escape URI for db query */
1145 char *escaped_uri;
1146 /* Escape value, lang and datatype for db query */
1147 char *escaped_value, *escaped_lang, *escaped_datatype;
1148 /* Escape name for db query */
1149 char *escaped_name;
1150 raptor_sequence *seq=NULL;
1151 librdf_hash_datum hd_key, hd_value; /* on stack - not allocated */
1152 librdf_hash_datum* old_value;
1153 pending_row* prow;
1154
1155 /* Get MySQL connection handle */
1156 handle=librdf_storage_mysql_get_handle(storage);
1157 if(!handle)
1158 return 0;
1159
1160 /* Get hash */
1161 switch(type) {
1162 case LIBRDF_NODE_TYPE_RESOURCE:
1163 node_type=TRIPLE_URI;
1164
1165 uri=librdf_uri_as_counted_string(librdf_node_get_uri(node), &nodelen);
1166 hash=librdf_storage_mysql_hash(storage, "R", (char*)uri, nodelen);
1167 break;
1168
1169 case LIBRDF_NODE_TYPE_LITERAL:
1170 node_type=TRIPLE_LITERAL;
1171
1172 value=librdf_node_get_literal_value_as_counted_string(node, &valuelen);
1173 lang=librdf_node_get_literal_value_language(node);
1174 if(lang)
1175 langlen=strlen(lang);
1176 dt=librdf_node_get_literal_value_datatype_uri(node);
1177 if(dt)
1178 datatype=librdf_uri_as_counted_string(dt, &datatypelen);
1179
1180 /* Create composite node string for hash generation */
1181 nodestring = LIBRDF_MALLOC(char*, valuelen + langlen + datatypelen + 3);
1182 if(!nodestring) {
1183 librdf_storage_mysql_release_handle(storage, handle);
1184 return 0;
1185 }
1186 strcpy(nodestring, (const char*)value);
1187 strcat(nodestring, "<");
1188 if(lang)
1189 strcat(nodestring, lang);
1190 strcat(nodestring, ">");
1191 if(datatype)
1192 strcat(nodestring, (const char*)datatype);
1193 nodelen=valuelen+langlen+datatypelen+2;
1194 hash=librdf_storage_mysql_hash(storage, "L", nodestring, nodelen);
1195 LIBRDF_FREE(char*, nodestring);
1196 break;
1197
1198 case LIBRDF_NODE_TYPE_BLANK:
1199 node_type=TRIPLE_BLANK;
1200
1201 name=librdf_node_get_blank_identifier(node);
1202 nodelen=strlen((const char*)name);
1203 hash=librdf_storage_mysql_hash(storage, "B", (char*)name, nodelen);
1204 break;
1205
1206 case LIBRDF_NODE_TYPE_UNKNOWN:
1207 default:
1208 librdf_log(storage->world,
1209 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1210 "Do not know how to store node type %d", node->type);
1211 hash=0;
1212 goto tidy;
1213 }
1214
1215 if(mode != NODE_HASH_MODE_STORE_NODE)
1216 goto tidy;
1217
1218
1219 table=&mysql_tables[node_type];
1220
1221 if(context->transaction_handle) {
1222 /* In a transaction, check this node has not already been handled */
1223
1224 /* Store the new */
1225 hd_key.data=&hash;
1226 hd_key.size=sizeof(u64);
1227
1228 /* if existing hash found, do not add it */
1229 if((old_value=librdf_hash_get_one(context->pending_insert_hash_nodes,
1230 &hd_key))) {
1231 #ifdef LIBRDF_DEBUG_SQL
1232 LIBRDF_DEBUG2("Already seen node with hash " UINT64_T_FMT " - not inserting\n", hash);
1233 #endif
1234 librdf_free_hash_datum(old_value);
1235 goto tidy;
1236 }
1237
1238 hd_value.data=(void*)"1";
1239 hd_value.size=2;
1240 /* store in hash: 'hash'(u64) => "1" */
1241 if(librdf_hash_put(context->pending_insert_hash_nodes,
1242 &hd_key, &hd_value)) {
1243 hash=0;
1244 goto tidy;
1245 }
1246
1247 /* Store in pending inserts sequence */
1248 seq=context->pending_inserts[node_type];
1249 } else {
1250 /* not a transaction - store in temporary sequence */
1251 seq = raptor_new_sequence((raptor_data_free_handler)free_pending_row, NULL);
1252 }
1253
1254
1255 prow = LIBRDF_CALLOC(pending_row*, 1, sizeof(*prow));
1256 prow->key_len=1;
1257 prow->uints[0]=hash;
1258
1259 switch(type) {
1260 case LIBRDF_NODE_TYPE_RESOURCE:
1261 escaped_uri = LIBRDF_MALLOC(char*, nodelen * 2 + 1);
1262 if(!escaped_uri) {
1263 hash=0;
1264 goto tidy;
1265 }
1266 mysql_real_escape_string(handle, escaped_uri,
1267 (const char*)uri, nodelen);
1268
1269 prow->strings[0]=escaped_uri;
1270 prow->strings_len[0]=strlen(escaped_uri);
1271 prow->strings_count=1;
1272 break;
1273
1274 case LIBRDF_NODE_TYPE_LITERAL:
1275 escaped_value = LIBRDF_MALLOC(char*, valuelen * 2 + 1);
1276 escaped_lang = LIBRDF_MALLOC(char*, langlen * 2 + 1);
1277 escaped_datatype = LIBRDF_MALLOC(char*, datatypelen * 2 + 1);
1278
1279 if(!escaped_value || !escaped_lang || !escaped_datatype) {
1280 hash=0;
1281 goto tidy;
1282 }
1283 mysql_real_escape_string(handle, escaped_value,
1284 (const char*)value, valuelen);
1285 if(lang)
1286 mysql_real_escape_string(handle, escaped_lang,
1287 (const char*)lang, langlen);
1288 else
1289 strcpy(escaped_lang,"");
1290 if(datatype)
1291 mysql_real_escape_string(handle, escaped_datatype,
1292 (const char*)datatype, datatypelen);
1293 else
1294 strcpy(escaped_datatype,"");
1295
1296 prow->strings[0]=escaped_value;
1297 prow->strings_len[0]=strlen(escaped_value);
1298 prow->strings[1]=escaped_lang;
1299 prow->strings_len[1]=strlen(escaped_lang);
1300 prow->strings[2]=escaped_datatype;
1301 prow->strings_len[2]=strlen(escaped_datatype);
1302 prow->strings_count=3;
1303 break;
1304
1305 case LIBRDF_NODE_TYPE_BLANK:
1306 escaped_name = LIBRDF_MALLOC(char*, nodelen * 2 + 1);
1307 if(!escaped_name) {
1308 hash=0;
1309 goto tidy;
1310 }
1311 mysql_real_escape_string(handle, escaped_name,
1312 (const char*)name, nodelen);
1313
1314 prow->strings[0]=escaped_name;
1315 prow->strings_len[0]=strlen(escaped_name);
1316 prow->strings_count=1;
1317 break;
1318
1319 case LIBRDF_NODE_TYPE_UNKNOWN:
1320 default:
1321 librdf_log(storage->world,
1322 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1323 "Do not know how to store node type %d", node->type);
1324 hash=0;
1325 goto tidy;
1326
1327 }
1328
1329 raptor_sequence_push(seq, prow);
1330
1331
1332 if(context->transaction_handle) {
1333 /* in a transaction */
1334 } else {
1335 /* not in a transaction so run it now */
1336 raptor_stringbuffer *sb=NULL;
1337 size_t query_len;
1338
1339 sb=format_pending_row_sequence(table, seq);
1340
1341 query_len=raptor_stringbuffer_length(sb);
1342 query=(char*)raptor_stringbuffer_as_string(sb);
1343 if(query) {
1344 #ifdef LIBRDF_DEBUG_SQL
1345 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
1346 #endif
1347 if(mysql_real_query(handle, query, query_len) &&
1348 mysql_errno(handle) != ER_DUP_ENTRY) {
1349 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE,
1350 NULL, "MySQL insert into %s failed with error %s",
1351 table->name, mysql_error(handle));
1352 raptor_free_stringbuffer(sb);
1353 hash=0;
1354 goto tidy;
1355 }
1356 }
1357
1358 raptor_free_stringbuffer(sb);
1359 }
1360
1361 tidy:
1362 if(!context->transaction_handle) {
1363 /* if not in a transaction, lose this */
1364 if(seq)
1365 raptor_free_sequence(seq);
1366 }
1367
1368 if(handle) {
1369 librdf_storage_mysql_release_handle(storage, handle);
1370 }
1371
1372 return hash;
1373 }
1374
1375
1376 static u64
librdf_storage_mysql_get_node_hash(librdf_storage * storage,librdf_node * node)1377 librdf_storage_mysql_get_node_hash(librdf_storage* storage,
1378 librdf_node* node)
1379 {
1380 return librdf_storage_mysql_node_hash_common(storage, node,
1381 NODE_HASH_MODE_GET_HASH);
1382 }
1383
1384 static u64
librdf_storage_mysql_store_node(librdf_storage * storage,librdf_node * node)1385 librdf_storage_mysql_store_node(librdf_storage* storage,
1386 librdf_node* node)
1387 {
1388 return librdf_storage_mysql_node_hash_common(storage, node,
1389 NODE_HASH_MODE_STORE_NODE);
1390 }
1391
1392
1393 /*
1394 * librdf_storage_mysql_start_bulk - Prepare for bulk insert operation
1395 * @storage: the storage
1396 *
1397 * Return value: Non-zero on failure.
1398 */
1399 static int
librdf_storage_mysql_start_bulk(librdf_storage * storage)1400 librdf_storage_mysql_start_bulk(librdf_storage* storage)
1401 {
1402 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
1403 char disable_statement_keys[]="ALTER TABLE Statements" UINT64_T_FMT " DISABLE KEYS";
1404 char disable_literal_keys[]="ALTER TABLE Literals DISABLE KEYS";
1405 char lock_tables[]="LOCK TABLES Statements" UINT64_T_FMT " WRITE, Resources WRITE, Bnodes WRITE, Literals WRITE";
1406 char lock_tables_extra[]=", Statements WRITE";
1407 char *query=NULL;
1408 MYSQL *handle;
1409
1410 /* Get MySQL connection handle */
1411 handle=librdf_storage_mysql_get_handle(storage);
1412 if(!handle)
1413 return 1;
1414
1415 query = LIBRDF_MALLOC(char*, strlen(disable_statement_keys) + 21);
1416 if(!query) {
1417 librdf_storage_mysql_release_handle(storage, handle);
1418 return 1;
1419 }
1420 sprintf(query, disable_statement_keys, context->model);
1421
1422 #ifdef LIBRDF_DEBUG_SQL
1423 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
1424 #endif
1425 if(mysql_real_query(handle, query, strlen(query))) {
1426 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1427 "MySQL statement key disabling failed: %s",
1428 mysql_error(handle));
1429 librdf_storage_mysql_release_handle(storage, handle);
1430 return -1;
1431 }
1432 LIBRDF_FREE(char*, query);
1433
1434 #ifdef LIBRDF_DEBUG_SQL
1435 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
1436 #endif
1437 if(mysql_real_query(handle, disable_literal_keys,
1438 strlen(disable_literal_keys))) {
1439 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1440 "MySQL literal key disabling failed: %s",
1441 mysql_error(handle));
1442 librdf_storage_mysql_release_handle(storage, handle);
1443 return -1;
1444 }
1445
1446 query = LIBRDF_MALLOC(char*, strlen(lock_tables) +
1447 strlen(lock_tables_extra) + 21);
1448 if(!query) {
1449 librdf_storage_mysql_release_handle(storage, handle);
1450 return 1;
1451 }
1452 sprintf(query, lock_tables, context->model);
1453 if(context->merge)
1454 strcat(query, lock_tables_extra);
1455
1456 #ifdef LIBRDF_DEBUG_SQL
1457 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
1458 #endif
1459 if(mysql_real_query(handle, query, strlen(query))) {
1460 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1461 "MySQL table locking failed: %s",
1462 mysql_error(handle));
1463 LIBRDF_FREE(char*, query);
1464 librdf_storage_mysql_release_handle(storage, handle);
1465 return -1;
1466 }
1467 LIBRDF_FREE(char*, query);
1468
1469 librdf_storage_mysql_release_handle(storage, handle);
1470
1471 return 0;
1472 }
1473
1474
1475 /*
1476 * librdf_storage_mysql_stop_bulk - End bulk insert operation
1477 * @storage: the storage
1478 *
1479 * Return value: Non-zero on failure.
1480 */
1481 static int
librdf_storage_mysql_stop_bulk(librdf_storage * storage)1482 librdf_storage_mysql_stop_bulk(librdf_storage* storage)
1483 {
1484 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
1485 char enable_statement_keys[]="ALTER TABLE Statements" UINT64_T_FMT " ENABLE KEYS";
1486 char enable_literal_keys[]="ALTER TABLE Literals ENABLE KEYS";
1487 char unlock_tables[]="UNLOCK TABLES";
1488 char flush_statements[]="FLUSH TABLE Statements";
1489 char *query=NULL;
1490 MYSQL *handle;
1491
1492 /* Get MySQL connection handle */
1493 handle=librdf_storage_mysql_get_handle(storage);
1494 if(!handle)
1495 return 1;
1496
1497 #ifdef LIBRDF_DEBUG_SQL
1498 LIBRDF_DEBUG2("SQL: >>%s<<\n", unlock_tables);
1499 #endif
1500 if(mysql_real_query(handle, unlock_tables,
1501 strlen(unlock_tables))) {
1502 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1503 "MySQL table unlocking failed: %s",
1504 mysql_error(handle));
1505 librdf_storage_mysql_release_handle(storage, handle);
1506 return 1;
1507 }
1508
1509 query = LIBRDF_MALLOC(char*, strlen(enable_statement_keys) + 21);
1510 if(!query) {
1511 librdf_storage_mysql_release_handle(storage, handle);
1512 return 1;
1513 }
1514 sprintf(query, enable_statement_keys, context->model);
1515
1516 #ifdef LIBRDF_DEBUG_SQL
1517 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
1518 #endif
1519 if(mysql_real_query(handle, query, strlen(query))) {
1520 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1521 "MySQL statement key re-enabling failed: %s",
1522 mysql_error(handle));
1523 librdf_storage_mysql_release_handle(storage, handle);
1524 return -1;
1525 }
1526 LIBRDF_FREE(char*, query);
1527
1528 #ifdef LIBRDF_DEBUG_SQL
1529 LIBRDF_DEBUG2("SQL: >>%s<<\n", enable_literal_keys);
1530 #endif
1531 if(mysql_real_query(handle, enable_literal_keys,
1532 strlen(enable_literal_keys))) {
1533 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1534 "MySQL literal key re-enabling failed: %s",
1535 mysql_error(handle));
1536 librdf_storage_mysql_release_handle(storage, handle);
1537 return -1;
1538 }
1539
1540 #ifdef LIBRDF_DEBUG_SQL
1541 LIBRDF_DEBUG2("SQL: >>%s<<\n", flush_statements);
1542 #endif
1543 if(context->merge && mysql_real_query(handle, flush_statements,
1544 strlen(flush_statements))) {
1545 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1546 "MySQL table flush failed: %s",
1547 mysql_error(handle));
1548 librdf_storage_mysql_release_handle(storage, handle);
1549 return -1;
1550 }
1551
1552 librdf_storage_mysql_release_handle(storage, handle);
1553 return 0;
1554 }
1555
1556
1557 /**
1558 * librdf_storage_mysql_context_add_statements:
1559 * @storage: the storage
1560 * @context_node: #librdf_node object
1561 * @statement_stream: the stream of statements
1562 *
1563 * .
1564 *
1565 * Add statements in stream to storage, with context.
1566 *
1567 * Return value: Non-zero on failure.
1568 **/
1569 static int
librdf_storage_mysql_context_add_statements(librdf_storage * storage,librdf_node * context_node,librdf_stream * statement_stream)1570 librdf_storage_mysql_context_add_statements(librdf_storage* storage,
1571 librdf_node* context_node,
1572 librdf_stream* statement_stream)
1573 {
1574 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
1575 u64 ctxt=0;
1576 int helper=0;
1577
1578 /* Optimize for bulk loads? */
1579 if(context->bulk) {
1580 if(librdf_storage_mysql_start_bulk(storage))
1581 return 1;
1582 }
1583
1584 /* Find hash for context, creating if necessary */
1585 if(context_node) {
1586 ctxt=librdf_storage_mysql_store_node(storage,context_node);
1587 if(!ctxt)
1588 return 1;
1589 }
1590
1591 while(!helper && !librdf_stream_end(statement_stream)) {
1592 librdf_statement* statement=librdf_stream_get_object(statement_stream);
1593 helper=librdf_storage_mysql_context_add_statement_helper(storage, ctxt,
1594 statement);
1595 librdf_stream_next(statement_stream);
1596 }
1597
1598 return helper;
1599 }
1600
1601
1602 /**
1603 * librdf_storage_mysql_context_add_statement:
1604 * @storage: #librdf_storage object
1605 * @context_node: #librdf_node object
1606 * @statement: #librdf_statement statement to add
1607 *
1608 * Add a statement to a storage context.
1609 *
1610 * Return value: non 0 on failure
1611 **/
1612 static int
librdf_storage_mysql_context_add_statement(librdf_storage * storage,librdf_node * context_node,librdf_statement * statement)1613 librdf_storage_mysql_context_add_statement(librdf_storage* storage,
1614 librdf_node* context_node,
1615 librdf_statement* statement)
1616 {
1617 u64 ctxt=0;
1618
1619 /* Find hash for context, creating if necessary */
1620 if(context_node) {
1621 ctxt=librdf_storage_mysql_store_node(storage,context_node);
1622 if(!ctxt)
1623 return 1;
1624 }
1625
1626 return librdf_storage_mysql_context_add_statement_helper(storage, ctxt,
1627 statement);
1628 }
1629
1630
1631 /*
1632 * librdf_storage_mysql_context_add_statement_helper - Perform actual addition of a statement to a storage context
1633 * @storage: #librdf_storage object
1634 * @ctxt: u64 context hash
1635 * @statement: #librdf_statement statement to add
1636 *
1637 * Return value: non-zero on failure
1638 **/
1639 static int
librdf_storage_mysql_context_add_statement_helper(librdf_storage * storage,u64 ctxt,librdf_statement * statement)1640 librdf_storage_mysql_context_add_statement_helper(librdf_storage* storage,
1641 u64 ctxt, librdf_statement* statement)
1642 {
1643 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
1644 char insert_statement[]="INSERT INTO Statements" UINT64_T_FMT " (Subject,Predicate,Object,Context) VALUES (" UINT64_T_FMT "," UINT64_T_FMT "," UINT64_T_FMT "," UINT64_T_FMT ")";
1645 u64 subject, predicate, object;
1646 char *query=NULL;
1647 MYSQL *handle=NULL;
1648 int rc=0;
1649
1650 /* Get MySQL connection handle */
1651 handle=librdf_storage_mysql_get_handle(storage);
1652 if(!handle)
1653 return 1;
1654
1655 /* Find hashes for nodes, creating if necessary */
1656 subject=librdf_storage_mysql_store_node(storage,
1657 librdf_statement_get_subject(statement));
1658 predicate=librdf_storage_mysql_store_node(storage,
1659 librdf_statement_get_predicate(statement));
1660 object=librdf_storage_mysql_store_node(storage,
1661 librdf_statement_get_object(statement));
1662 if(!subject || !predicate || !object) {
1663 rc=1;
1664 goto tidy;
1665 }
1666
1667 if(context->transaction_handle) {
1668 /* in a transaction */
1669 pending_row* prow;
1670
1671 prow = LIBRDF_CALLOC(pending_row*, 1, sizeof(*prow));
1672 prow->key_len=4;
1673 prow->uints[0]=subject;
1674 prow->uints[1]=predicate;
1675 prow->uints[2]=object;
1676 prow->uints[3]=ctxt;
1677 raptor_sequence_push(context->pending_statements, prow);
1678
1679 } else {
1680 /* not a transaction - add statement to storage */
1681 query = LIBRDF_MALLOC(char*, strlen(insert_statement) + 101);
1682 if(!query) {
1683 rc=1;
1684 goto tidy;
1685 }
1686 sprintf(query, insert_statement, context->model, subject, predicate, object, ctxt);
1687
1688 #ifdef LIBRDF_DEBUG_SQL
1689 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
1690 #endif
1691 if(mysql_real_query(handle, query, strlen(query))) {
1692 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1693 "MySQL insert into Statements failed: %s",
1694 mysql_error(handle));
1695 rc=-1;
1696 goto tidy;
1697 }
1698 }
1699
1700 tidy:
1701 if(query)
1702 LIBRDF_FREE(char*, query);
1703 if(handle) {
1704 librdf_storage_mysql_release_handle(storage, handle);
1705 }
1706
1707 return rc;
1708 }
1709
1710
1711 /**
1712 * librdf_storage_mysql_contains_statement:
1713 * @storage: the storage
1714 * @statement: a complete statement
1715 *
1716 * Test if a given complete statement is present in the model.
1717 *
1718 * Return value: Non-zero if the model contains the statement.
1719 **/
1720 static int
librdf_storage_mysql_contains_statement(librdf_storage * storage,librdf_statement * statement)1721 librdf_storage_mysql_contains_statement(librdf_storage* storage,
1722 librdf_statement* statement)
1723 {
1724 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
1725 char find_statement[]="SELECT 1 FROM Statements" UINT64_T_FMT " WHERE Subject=" UINT64_T_FMT " AND Predicate=" UINT64_T_FMT " AND Object=" UINT64_T_FMT " limit 1";
1726 u64 subject, predicate, object;
1727 char *query;
1728 MYSQL_RES *res;
1729 MYSQL *handle;
1730
1731 /* Get MySQL connection handle */
1732 handle=librdf_storage_mysql_get_handle(storage);
1733 if(!handle)
1734 return 0;
1735
1736 /* Find hashes for nodes */
1737 subject=librdf_storage_mysql_get_node_hash(storage,
1738 librdf_statement_get_subject(statement));
1739 predicate=librdf_storage_mysql_get_node_hash(storage,
1740 librdf_statement_get_predicate(statement));
1741 object=librdf_storage_mysql_get_node_hash(storage,
1742 librdf_statement_get_object(statement));
1743 if(!subject || !predicate || !object) {
1744 librdf_storage_mysql_release_handle(storage, handle);
1745 return 0;
1746 }
1747
1748 /* Check for statement */
1749 query = LIBRDF_MALLOC(char*, strlen(find_statement) + 81);
1750 if(!query) {
1751 librdf_storage_mysql_release_handle(storage, handle);
1752 return 0;
1753 }
1754 sprintf(query, find_statement, context->model, subject, predicate, object);
1755
1756 #ifdef LIBRDF_DEBUG_SQL
1757 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
1758 #endif
1759 if(mysql_real_query(handle, query, strlen(query))) {
1760 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1761 "MySQL query for statement failed: %s",
1762 mysql_error(handle));
1763 LIBRDF_FREE(char*, query);
1764
1765 librdf_storage_mysql_release_handle(storage, handle);
1766 return 0;
1767 }
1768 LIBRDF_FREE(char*, query);
1769 if(!(res=mysql_store_result(handle)) ||
1770 !(mysql_fetch_row(res))) {
1771 if(res)
1772 mysql_free_result(res);
1773
1774 librdf_storage_mysql_release_handle(storage, handle);
1775 return 0;
1776 }
1777 if(res)
1778 mysql_free_result(res);
1779
1780 librdf_storage_mysql_release_handle(storage, handle);
1781
1782 return 1;
1783 }
1784
1785
1786 /**
1787 * librdf_storage_mysql_remove_statement:
1788 * @storage: #librdf_storage object
1789 * @statement: #librdf_statement statement to remove
1790 *
1791 * Remove a statement from storage.
1792 *
1793 * Return value: non-zero on failure
1794 **/
1795 static int
librdf_storage_mysql_remove_statement(librdf_storage * storage,librdf_statement * statement)1796 librdf_storage_mysql_remove_statement(librdf_storage* storage, librdf_statement* statement)
1797 {
1798 return librdf_storage_mysql_context_remove_statement(storage,NULL,statement);
1799 }
1800
1801
1802 /**
1803 * librdf_storage_mysql_context_remove_statement:
1804 * @storage: #librdf_storage object
1805 * @context_node: #librdf_node object
1806 * @statement: #librdf_statement statement to remove
1807 *
1808 * Remove a statement from a storage context.
1809 *
1810 * Return value: non-zero on failure
1811 **/
1812 static int
librdf_storage_mysql_context_remove_statement(librdf_storage * storage,librdf_node * context_node,librdf_statement * statement)1813 librdf_storage_mysql_context_remove_statement(librdf_storage* storage,
1814 librdf_node* context_node,
1815 librdf_statement* statement)
1816 {
1817 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
1818 char delete_statement[]="DELETE FROM Statements" UINT64_T_FMT " WHERE Subject=" UINT64_T_FMT " AND Predicate=" UINT64_T_FMT " AND Object=" UINT64_T_FMT;
1819 char delete_statement_with_context[]="DELETE FROM Statements" UINT64_T_FMT " WHERE Subject=" UINT64_T_FMT " AND Predicate=" UINT64_T_FMT " AND Object=" UINT64_T_FMT " AND Context=" UINT64_T_FMT;
1820 u64 subject, predicate, object, ctxt=0;
1821 char *query;
1822 MYSQL *handle;
1823
1824 /* Get MySQL connection handle */
1825 handle=librdf_storage_mysql_get_handle(storage);
1826 if(!handle)
1827 return 1;
1828
1829 /* Find hashes for nodes */
1830 subject=librdf_storage_mysql_get_node_hash(storage,
1831 librdf_statement_get_subject(statement));
1832 predicate=librdf_storage_mysql_get_node_hash(storage,
1833 librdf_statement_get_predicate(statement));
1834 object=librdf_storage_mysql_get_node_hash(storage,
1835 librdf_statement_get_object(statement));
1836 if(context_node) {
1837 ctxt=librdf_storage_mysql_get_node_hash(storage,context_node);
1838 if(!ctxt) {
1839 librdf_storage_mysql_release_handle(storage, handle);
1840 return 1;
1841 }
1842 }
1843 if(!subject || !predicate || !object || (context_node && !ctxt)) {
1844 librdf_storage_mysql_release_handle(storage, handle);
1845 return 1;
1846 }
1847
1848 /* Remove statement(s) from storage */
1849 if(context_node) {
1850 query = LIBRDF_MALLOC(char*, strlen(delete_statement_with_context) + 101);
1851 if(!query) {
1852 librdf_storage_mysql_release_handle(storage, handle);
1853 return 1;
1854 }
1855 sprintf(query, delete_statement_with_context, context->model, subject,
1856 predicate, object, ctxt);
1857 } else {
1858 query = LIBRDF_MALLOC(char*, strlen(delete_statement) + 81);
1859 if(!query) {
1860 librdf_storage_mysql_release_handle(storage, handle);
1861 return 1;
1862 }
1863 sprintf(query, delete_statement, context->model, subject, predicate,
1864 object);
1865 }
1866
1867 #ifdef LIBRDF_DEBUG_SQL
1868 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
1869 #endif
1870 if(mysql_real_query(handle, query, strlen(query))) {
1871 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1872 "MySQL delete from Statements failed: %s",
1873 mysql_error(handle));
1874 LIBRDF_FREE(char*, query);
1875
1876 librdf_storage_mysql_release_handle(storage, handle);
1877 return -1;
1878 }
1879 LIBRDF_FREE(char*, query);
1880
1881 librdf_storage_mysql_release_handle(storage, handle);
1882
1883 return 0;
1884 }
1885
1886
1887 /**
1888 * librdf_storage_mysql_context_remove_statements:
1889 * @storage: #librdf_storage object
1890 * @context_node: #librdf_node object
1891 *
1892 * Remove all statement from a storage context.
1893 *
1894 * Return value: non-zero on failure
1895 **/
1896 static int
librdf_storage_mysql_context_remove_statements(librdf_storage * storage,librdf_node * context_node)1897 librdf_storage_mysql_context_remove_statements(librdf_storage* storage,
1898 librdf_node* context_node)
1899 {
1900 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
1901 char delete_context[]="DELETE FROM Statements" UINT64_T_FMT " WHERE Context=" UINT64_T_FMT;
1902 char delete_model[]="DELETE FROM Statements" UINT64_T_FMT;
1903 char flush_statements[]="FLUSH TABLE Statements";
1904 u64 ctxt=0;
1905 char *query;
1906 MYSQL *handle;
1907
1908 /* Get MySQL connection handle */
1909 handle=librdf_storage_mysql_get_handle(storage);
1910 if(!handle)
1911 return 1;
1912
1913 /* Find hash for context */
1914 if(context_node) {
1915 ctxt=librdf_storage_mysql_get_node_hash(storage,context_node);
1916 if(!ctxt) {
1917 librdf_storage_mysql_release_handle(storage, handle);
1918 return 1;
1919 }
1920 }
1921
1922 /* Remove statement(s) from storage */
1923 if(context_node) {
1924 query = LIBRDF_MALLOC(char*, strlen(delete_context) + 61);
1925 if(!query) {
1926 librdf_storage_mysql_release_handle(storage, handle);
1927 return 1;
1928 }
1929 sprintf(query, delete_context, context->model, ctxt);
1930 } else {
1931 query = LIBRDF_MALLOC(char*, strlen(delete_model) + 21);
1932 if(!query) {
1933 librdf_storage_mysql_release_handle(storage, handle);
1934 return 1;
1935 }
1936 sprintf(query, delete_model, context->model);
1937 }
1938
1939 #ifdef LIBRDF_DEBUG_SQL
1940 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
1941 #endif
1942 if(mysql_real_query(handle,query,strlen(query))) {
1943 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1944 "MySQL delete of context from Statements failed: %s",
1945 mysql_error(handle));
1946 LIBRDF_FREE(char*, query);
1947
1948 librdf_storage_mysql_release_handle(storage, handle);
1949 return -1;
1950 }
1951 LIBRDF_FREE(char*, query);
1952
1953 /* Flush merge table when using delete without where... */
1954 #ifdef LIBRDF_DEBUG_SQL
1955 LIBRDF_DEBUG2("SQL: >>%s<<\n", flush_statements);
1956 #endif
1957 if(context->merge && !context_node
1958 && mysql_real_query(handle, flush_statements,
1959 strlen(flush_statements))) {
1960 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
1961 "MySQL table flush failed: %s",
1962 mysql_error(handle));
1963 librdf_storage_mysql_release_handle(storage, handle);
1964 return -1;
1965 }
1966
1967 librdf_storage_mysql_release_handle(storage, handle);
1968
1969 return 0;
1970 }
1971
1972
1973 /**
1974 * librdf_storage_mysql_serialise:
1975 * @storage: the storage
1976 *
1977 * Return a stream of all statements in a storage.
1978 *
1979 * Return a stream of all statements in a storage.
1980 *
1981 * Return value: a #librdf_stream or NULL on failure
1982 **/
1983 static librdf_stream*
librdf_storage_mysql_serialise(librdf_storage * storage)1984 librdf_storage_mysql_serialise(librdf_storage* storage)
1985 {
1986 return librdf_storage_mysql_find_statements_in_context(storage,NULL,NULL);
1987 }
1988
1989
1990 /**
1991 * librdf_storage_mysql_find_statements:
1992 * @storage: the storage
1993 * @statement: the statement to match
1994 *
1995 * Find a graph of statements in storage.
1996 *
1997 * Return a stream of statements matching the given statement (or
1998 * all statements if NULL). Parts (subject, predicate, object) of the
1999 * statement can be empty in which case any statement part will match that.
2000 *
2001 * Return value: a #librdf_stream or NULL on failure
2002 **/
2003 static librdf_stream*
librdf_storage_mysql_find_statements(librdf_storage * storage,librdf_statement * statement)2004 librdf_storage_mysql_find_statements(librdf_storage* storage,
2005 librdf_statement* statement)
2006 {
2007 return librdf_storage_mysql_find_statements_in_context(storage,statement,NULL);
2008 }
2009
2010
2011 /**
2012 * librdf_storage_mysql_context_serialise:
2013 * @storage: #librdf_storage object
2014 * @context_node: #librdf_node object
2015 *
2016 * List all statements in a storage context.
2017 *
2018 * Return value: #librdf_stream of statements or NULL on failure or context is empty
2019 **/
2020 static librdf_stream*
librdf_storage_mysql_context_serialise(librdf_storage * storage,librdf_node * context_node)2021 librdf_storage_mysql_context_serialise(librdf_storage* storage,
2022 librdf_node* context_node)
2023 {
2024 return librdf_storage_mysql_find_statements_in_context(storage,NULL,context_node);
2025 }
2026
2027
2028 /**
2029 * librdf_storage_mysql_find_statements_in_context:
2030 * @storage: the storage
2031 * @statement: the statement to match
2032 * @context_node: the context to search
2033 *
2034 * Find a graph of statements in a storage context.
2035 *
2036 * Return a stream of statements matching the given statement (or
2037 * all statements if NULL). Parts (subject, predicate, object) of the
2038 * statement can be empty in which case any statement part will match that.
2039 *
2040 * Return value: a #librdf_stream or NULL on failure
2041 **/
2042 static librdf_stream*
librdf_storage_mysql_find_statements_in_context(librdf_storage * storage,librdf_statement * statement,librdf_node * context_node)2043 librdf_storage_mysql_find_statements_in_context(librdf_storage* storage, librdf_statement* statement,
2044 librdf_node* context_node)
2045 {
2046 return librdf_storage_mysql_find_statements_with_options(storage, statement, context_node, NULL);
2047 }
2048
2049
2050 /**
2051 * librdf_storage_mysql_find_statements_with_options:
2052 * @storage: the storage
2053 * @statement: the statement to match
2054 * @context_node: the context to search
2055 * @options: #librdf_hash of match options or NULL
2056 *
2057 * Find a graph of statements in a storage context with options.
2058 *
2059 * Return a stream of statements matching the given statement (or
2060 * all statements if NULL). Parts (subject, predicate, object) of the
2061 * statement can be empty in which case any statement part will match that.
2062 *
2063 * Return value: a #librdf_stream or NULL on failure
2064 **/
2065 static librdf_stream*
librdf_storage_mysql_find_statements_with_options(librdf_storage * storage,librdf_statement * statement,librdf_node * context_node,librdf_hash * options)2066 librdf_storage_mysql_find_statements_with_options(librdf_storage* storage,
2067 librdf_statement* statement,
2068 librdf_node* context_node,
2069 librdf_hash* options)
2070 {
2071 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
2072 librdf_storage_mysql_sos_context* sos;
2073 librdf_node *subject=NULL, *predicate=NULL, *object=NULL;
2074 char *query;
2075 char tmp[64];
2076 char where[256];
2077 char joins[640];
2078 librdf_stream *stream;
2079
2080 /* Initialize sos context */
2081 sos = LIBRDF_CALLOC(librdf_storage_mysql_sos_context*, 1, sizeof(*sos));
2082 if(!sos)
2083 return NULL;
2084 sos->storage=storage;
2085 librdf_storage_add_reference(sos->storage);
2086
2087 if(statement)
2088 sos->query_statement=librdf_new_statement_from_statement(statement);
2089 if(context_node)
2090 sos->query_context=librdf_new_node_from_node(context_node);
2091 sos->current_statement=NULL;
2092 sos->current_context=NULL;
2093 sos->results=NULL;
2094
2095 if(options) {
2096 sos->is_literal_match=librdf_hash_get_as_boolean(options, "match-substring");
2097 }
2098
2099 /* Get MySQL connection handle */
2100 sos->handle=librdf_storage_mysql_get_handle(storage);
2101 if(!sos->handle) {
2102 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2103 return NULL;
2104 }
2105
2106 /* Construct query */
2107 query = LIBRDF_MALLOC(char*, 21);
2108 if(!query) {
2109 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2110 return NULL;
2111 }
2112 strcpy(query, "SELECT");
2113 *where='\0';
2114 if(sos->is_literal_match)
2115 sprintf(joins, " FROM Literals AS L LEFT JOIN Statements" UINT64_T_FMT " as S ON L.ID=S.Object",
2116 context->model);
2117 else
2118 sprintf(joins, " FROM Statements" UINT64_T_FMT " AS S", context->model);
2119
2120 if(statement) {
2121 subject=librdf_statement_get_subject(statement);
2122 predicate=librdf_statement_get_predicate(statement);
2123 object=librdf_statement_get_object(statement);
2124 }
2125
2126 /* Subject */
2127 if(statement && subject) {
2128 sprintf(tmp, "S.Subject=" UINT64_T_FMT "",
2129 librdf_storage_mysql_get_node_hash(storage,subject));
2130 if(!strlen(where))
2131 strcat(where, " WHERE ");
2132 else
2133 strcat(where, " AND ");
2134 strcat(where, tmp);
2135 } else {
2136 if(librdf_storage_mysql_find_statements_in_context_augment_query(&query, " SubjectR.URI AS SuR, SubjectB.Name AS SuB")) {
2137 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2138 return NULL;
2139 }
2140 strcat(joins," LEFT JOIN Resources AS SubjectR ON S.Subject=SubjectR.ID");
2141 strcat(joins," LEFT JOIN Bnodes AS SubjectB ON S.Subject=SubjectB.ID");
2142 }
2143
2144 /* Predicate */
2145 if(statement && predicate) {
2146 sprintf(tmp, "S.Predicate=" UINT64_T_FMT "",
2147 librdf_storage_mysql_get_node_hash(storage, predicate));
2148 if(!strlen(where))
2149 strcat(where, " WHERE ");
2150 else
2151 strcat(where, " AND ");
2152 strcat(where, tmp);
2153 } else {
2154 if(!statement || !subject) {
2155 if(librdf_storage_mysql_find_statements_in_context_augment_query(&query, ",")) {
2156 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2157 return NULL;
2158 }
2159 }
2160 if(librdf_storage_mysql_find_statements_in_context_augment_query(&query, " PredicateR.URI AS PrR")) {
2161 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2162 return NULL;
2163 }
2164 strcat(joins," LEFT JOIN Resources AS PredicateR ON S.Predicate=PredicateR.ID");
2165 }
2166
2167 /* Object */
2168 if(statement && object) {
2169 if(!sos->is_literal_match) {
2170 sprintf(tmp,"S.Object=" UINT64_T_FMT "",
2171 librdf_storage_mysql_get_node_hash(storage, object));
2172 if(!strlen(where))
2173 strcat(where, " WHERE ");
2174 else
2175 strcat(where, " AND ");
2176 strcat(where, tmp);
2177 } else {
2178 /* MATCH literal, not hash_id */
2179 if(!statement || !subject || !predicate) {
2180 if(librdf_storage_mysql_find_statements_in_context_augment_query(&query, ",")) {
2181 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2182 return NULL;
2183 }
2184 }
2185 if(librdf_storage_mysql_find_statements_in_context_augment_query(&query, " ObjectR.URI AS ObR, ObjectB.Name AS ObB, ObjectL.Value AS ObV, ObjectL.Language AS ObL, ObjectL.Datatype AS ObD")) {
2186 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2187 return NULL;
2188 }
2189 strcat(joins," LEFT JOIN Resources AS ObjectR ON S.Object=ObjectR.ID");
2190 strcat(joins," LEFT JOIN Bnodes AS ObjectB ON S.Object=ObjectB.ID");
2191 strcat(joins," LEFT JOIN Literals AS ObjectL ON S.Object=ObjectL.ID");
2192
2193 sprintf(tmp, "MATCH(L.Value) AGAINST ('%s')",
2194 librdf_node_get_literal_value(object));
2195
2196 /* NOTE: This is NOT USED but could be if FULLTEXT wasn't enabled */
2197 /*
2198 sprintf(tmp, " L.Value LIKE '%%%s%%'",
2199 librdf_node_get_literal_value(object));
2200 */
2201 if(!strlen(where))
2202 strcat(where, " WHERE ");
2203 else
2204 strcat(where, " AND ");
2205 strcat(where, tmp);
2206 }
2207 } else {
2208 if(!statement || !subject || !predicate) {
2209 if(librdf_storage_mysql_find_statements_in_context_augment_query(&query, ",")) {
2210 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2211 return NULL;
2212 }
2213 }
2214 if(librdf_storage_mysql_find_statements_in_context_augment_query(&query, " ObjectR.URI AS ObR, ObjectB.Name AS ObB, ObjectL.Value AS ObV, ObjectL.Language AS ObL, ObjectL.Datatype AS ObD")) {
2215 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2216 return NULL;
2217 }
2218 strcat(joins," LEFT JOIN Resources AS ObjectR ON S.Object=ObjectR.ID");
2219 strcat(joins," LEFT JOIN Bnodes AS ObjectB ON S.Object=ObjectB.ID");
2220 strcat(joins," LEFT JOIN Literals AS ObjectL ON S.Object=ObjectL.ID");
2221 }
2222
2223 /* Context */
2224 if(context_node) {
2225 sprintf(tmp,"S.Context=" UINT64_T_FMT "",
2226 librdf_storage_mysql_get_node_hash(storage,context_node));
2227 if(!strlen(where))
2228 strcat(where, " WHERE ");
2229 else
2230 strcat(where, " AND ");
2231 strcat(where, tmp);
2232 } else {
2233 if(!statement || !subject || !predicate || !object) {
2234 if(librdf_storage_mysql_find_statements_in_context_augment_query(&query, ",")) {
2235 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2236 return NULL;
2237 }
2238 }
2239 if(librdf_storage_mysql_find_statements_in_context_augment_query(&query, " ContextR.URI AS CoR, ContextB.Name AS CoB, ContextL.Value AS CoV, ContextL.Language AS CoL, ContextL.Datatype AS CoD")) {
2240 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2241 return NULL;
2242 }
2243 strcat(joins," LEFT JOIN Resources AS ContextR ON S.Context=ContextR.ID");
2244 strcat(joins," LEFT JOIN Bnodes AS ContextB ON S.Context=ContextB.ID");
2245 strcat(joins," LEFT JOIN Literals AS ContextL ON S.Context=ContextL.ID");
2246 }
2247
2248 /* Query without variables? */
2249 if(statement && subject && predicate && object && context_node) {
2250 if(librdf_storage_mysql_find_statements_in_context_augment_query(&query, " 1")) {
2251 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2252 return NULL;
2253 }
2254 }
2255
2256 /* Complete query string */
2257 if(librdf_storage_mysql_find_statements_in_context_augment_query(&query, joins) ||
2258 librdf_storage_mysql_find_statements_in_context_augment_query(&query, where)) {
2259 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2260 return NULL;
2261 }
2262
2263 /* Start query... */
2264 #ifdef LIBRDF_DEBUG_SQL
2265 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
2266 #endif
2267 if(mysql_real_query(sos->handle, query, strlen(query)) ||
2268 !(sos->results=mysql_use_result(sos->handle))) {
2269 librdf_log(sos->storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
2270 "MySQL query failed: %s",
2271 mysql_error(sos->handle));
2272 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2273 return NULL;
2274 }
2275 LIBRDF_FREE(char*, query);
2276
2277 /* Get first statement, if any, and initialize stream */
2278 if(librdf_storage_mysql_find_statements_in_context_next_statement(sos) ||
2279 !sos->current_statement) {
2280 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2281 return librdf_new_empty_stream(storage->world);
2282 }
2283
2284 stream=librdf_new_stream(storage->world,(void*)sos,
2285 &librdf_storage_mysql_find_statements_in_context_end_of_stream,
2286 &librdf_storage_mysql_find_statements_in_context_next_statement,
2287 &librdf_storage_mysql_find_statements_in_context_get_statement,
2288 &librdf_storage_mysql_find_statements_in_context_finished);
2289 if(!stream) {
2290 librdf_storage_mysql_find_statements_in_context_finished((void*)sos);
2291 return NULL;
2292 }
2293
2294 return stream;
2295 }
2296
2297
2298 static int
librdf_storage_mysql_find_statements_in_context_augment_query(char ** query,const char * addition)2299 librdf_storage_mysql_find_statements_in_context_augment_query(char **query,
2300 const char *addition)
2301 {
2302 char *newquery;
2303
2304 /* Augment existing query, returning 0 on success. */
2305 newquery = LIBRDF_MALLOC(char*, strlen(*query) + strlen(addition) + 1);
2306 if(!newquery)
2307 return 1;
2308 strcpy(newquery,*query);
2309 strcat(newquery,addition);
2310 LIBRDF_FREE(char*, *query);
2311 *query=newquery;
2312
2313 return 0;
2314 }
2315
2316
2317 static int
librdf_storage_mysql_find_statements_in_context_end_of_stream(void * context)2318 librdf_storage_mysql_find_statements_in_context_end_of_stream(void* context)
2319 {
2320 librdf_storage_mysql_sos_context* sos=(librdf_storage_mysql_sos_context*)context;
2321
2322 return sos->current_statement==NULL;
2323 }
2324
2325
2326 static int
librdf_storage_mysql_find_statements_in_context_next_statement(void * context)2327 librdf_storage_mysql_find_statements_in_context_next_statement(void* context)
2328 {
2329 librdf_storage_mysql_sos_context* sos=(librdf_storage_mysql_sos_context*)context;
2330 MYSQL_ROW row;
2331 librdf_node *subject=NULL, *predicate=NULL, *object=NULL;
2332 librdf_node *node;
2333
2334 /* Get next statement */
2335 row=mysql_fetch_row(sos->results);
2336 if(row) {
2337 /* Get ready for context */
2338 if(sos->current_context)
2339 librdf_free_node(sos->current_context);
2340 sos->current_context=NULL;
2341 /* Is this a query with statement parts? */
2342 if(sos->query_statement) {
2343 subject=librdf_statement_get_subject(sos->query_statement);
2344 predicate=librdf_statement_get_predicate(sos->query_statement);
2345 if(sos->is_literal_match)
2346 object=NULL;
2347 else
2348 object=librdf_statement_get_object(sos->query_statement);
2349 }
2350 /* Make sure we have a statement object to return */
2351 if(!sos->current_statement) {
2352 if(!(sos->current_statement=librdf_new_statement(sos->storage->world)))
2353 return 1;
2354
2355 }
2356 librdf_statement_clear(sos->current_statement);
2357 /* Query without variables? */
2358 if(subject && predicate && object && sos->query_context) {
2359 librdf_statement_set_subject(sos->current_statement,librdf_new_node_from_node(subject));
2360 librdf_statement_set_predicate(sos->current_statement,librdf_new_node_from_node(predicate));
2361 librdf_statement_set_object(sos->current_statement,librdf_new_node_from_node(object));
2362 sos->current_context=librdf_new_node_from_node(sos->query_context);
2363 } else {
2364 /* Turn row parts into statement and context */
2365 int part=0;
2366 /* Subject - constant or from row? */
2367 if(subject) {
2368 librdf_statement_set_subject(sos->current_statement,librdf_new_node_from_node(subject));
2369 } else {
2370 /* Resource or Bnode? */
2371 if(row[part]) {
2372 if(!(node=librdf_new_node_from_uri_string(sos->storage->world,
2373 (const unsigned char*)row[part])))
2374 return 1;
2375 } else if(row[part+1]) {
2376 if(!(node=librdf_new_node_from_blank_identifier(sos->storage->world,
2377 (const unsigned char*)row[part+1])))
2378 return 1;
2379 } else
2380 return 1;
2381
2382 librdf_statement_set_subject(sos->current_statement,node);
2383 part+=2;
2384 }
2385 /* Predicate - constant or from row? */
2386 if(predicate) {
2387 librdf_statement_set_predicate(sos->current_statement,librdf_new_node_from_node(predicate));
2388 } else {
2389 /* Resource? */
2390 if(row[part]) {
2391 if(!(node=librdf_new_node_from_uri_string(sos->storage->world,
2392 (const unsigned char*)row[part])))
2393 return 1;
2394 } else
2395 return 1;
2396
2397 librdf_statement_set_predicate(sos->current_statement,node);
2398 part+=1;
2399 }
2400 /* Object - constant or from row? */
2401 if(object) {
2402 librdf_statement_set_object(sos->current_statement,librdf_new_node_from_node(object));
2403 } else {
2404 /* Resource, Bnode or Literal? */
2405 if(row[part]) {
2406 if(!(node=librdf_new_node_from_uri_string(sos->storage->world,
2407 (const unsigned char*)row[part])))
2408 return 1;
2409 } else if(row[part+1]) {
2410 if(!(node=librdf_new_node_from_blank_identifier(sos->storage->world,
2411 (const unsigned char*)row[part+1])))
2412 return 1;
2413 } else if(row[part+2]) {
2414 /* Typed literal? */
2415 librdf_uri *datatype=NULL;
2416 if(row[part+4] && strlen(row[part+4]))
2417 datatype=librdf_new_uri(sos->storage->world,
2418 (const unsigned char*)row[part+4]);
2419 if(!(node=librdf_new_node_from_typed_literal(sos->storage->world,
2420 (const unsigned char*)row[part+2],
2421 row[part+3],
2422 datatype)))
2423 return 1;
2424 } else
2425 return 1;
2426
2427 librdf_statement_set_object(sos->current_statement,node);
2428 part+=5;
2429 }
2430 /* Context - constant or from row? */
2431 if(sos->query_context) {
2432 sos->current_context=librdf_new_node_from_node(sos->query_context);
2433 } else {
2434 /* Resource, Bnode or Literal? */
2435 if(row[part]) {
2436 if(!(node=librdf_new_node_from_uri_string(sos->storage->world,
2437 (const unsigned char*)row[part])))
2438 return 1;
2439 } else if(row[part+1]) {
2440 if(!(node=librdf_new_node_from_blank_identifier(sos->storage->world,
2441 (const unsigned char*)row[part+1])))
2442 return 1;
2443 } else if(row[part+2]) {
2444 /* Typed literal? */
2445 librdf_uri *datatype=NULL;
2446 if(row[part+4] && strlen(row[part+4]))
2447 datatype=librdf_new_uri(sos->storage->world,
2448 (const unsigned char*)row[part+4]);
2449 if(!(node=librdf_new_node_from_typed_literal(sos->storage->world,
2450 (const unsigned char*)row[part+2],
2451 row[part+3],
2452 datatype)))
2453 return 1;
2454 } else
2455 /* no context */
2456 node=NULL;
2457 sos->current_context=node;
2458 }
2459 }
2460 } else {
2461 if(sos->current_statement)
2462 librdf_free_statement(sos->current_statement);
2463 sos->current_statement=NULL;
2464 if(sos->current_context)
2465 librdf_free_node(sos->current_context);
2466 sos->current_context=NULL;
2467 }
2468
2469 return 0;
2470 }
2471
2472
2473 static void*
librdf_storage_mysql_find_statements_in_context_get_statement(void * context,int flags)2474 librdf_storage_mysql_find_statements_in_context_get_statement(void* context, int flags)
2475 {
2476 librdf_storage_mysql_sos_context* sos=(librdf_storage_mysql_sos_context*)context;
2477
2478 switch(flags) {
2479 case LIBRDF_ITERATOR_GET_METHOD_GET_OBJECT:
2480 return sos->current_statement;
2481 case LIBRDF_ITERATOR_GET_METHOD_GET_CONTEXT:
2482 return sos->current_context;
2483 default:
2484 LIBRDF_DEBUG2("Unknown flags %d\n", flags);
2485 return NULL;
2486 }
2487 }
2488
2489
2490 static void
librdf_storage_mysql_find_statements_in_context_finished(void * context)2491 librdf_storage_mysql_find_statements_in_context_finished(void* context)
2492 {
2493 librdf_storage_mysql_sos_context* sos=(librdf_storage_mysql_sos_context*)context;
2494
2495 if(sos->results)
2496 mysql_free_result(sos->results);
2497
2498 if(sos->handle) {
2499 librdf_storage_mysql_release_handle(sos->storage, sos->handle);
2500 }
2501
2502 if(sos->current_statement)
2503 librdf_free_statement(sos->current_statement);
2504
2505 if(sos->current_context)
2506 librdf_free_node(sos->current_context);
2507
2508 if(sos->query_statement)
2509 librdf_free_statement(sos->query_statement);
2510
2511 if(sos->query_context)
2512 librdf_free_node(sos->query_context);
2513
2514 if(sos->storage)
2515 librdf_storage_remove_reference(sos->storage);
2516
2517 LIBRDF_FREE(librdf_storage_mysql_sos_context, sos);
2518 }
2519
2520
2521 /**
2522 * librdf_storage_mysql_get_contexts:
2523 * @storage: the storage
2524 *
2525 * Return an iterator with the context nodes present in storage.
2526 *
2527 * Return value: a #librdf_iterator or NULL on failure
2528 **/
2529 static librdf_iterator*
librdf_storage_mysql_get_contexts(librdf_storage * storage)2530 librdf_storage_mysql_get_contexts(librdf_storage* storage)
2531 {
2532 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance*)storage->instance;
2533 librdf_storage_mysql_get_contexts_context* gccontext;
2534 const char select_contexts[]="\
2535 SELECT DISTINCT R.URI AS CoR, B.Name AS CoB, \
2536 L.Value AS CoV, L.Language AS CoL, L.Datatype AS CoD \
2537 FROM Statements" UINT64_T_FMT " as S \
2538 LEFT JOIN Resources AS R ON S.Context=R.ID \
2539 LEFT JOIN Bnodes AS B ON S.Context=B.ID \
2540 LEFT JOIN Literals AS L ON S.Context=L.ID";
2541 char *query;
2542 librdf_iterator* iterator;
2543
2544 /* Initialize get_contexts context */
2545 gccontext = LIBRDF_CALLOC(librdf_storage_mysql_get_contexts_context*, 1,
2546 sizeof(*gccontext));
2547 if(!gccontext)
2548 return NULL;
2549 gccontext->storage=storage;
2550 librdf_storage_add_reference(gccontext->storage);
2551
2552 gccontext->current_context=NULL;
2553 gccontext->results=NULL;
2554
2555 /* Get MySQL connection handle */
2556 gccontext->handle=librdf_storage_mysql_get_handle(storage);
2557 if(!gccontext->handle) {
2558 librdf_storage_mysql_get_contexts_finished((void*)gccontext);
2559 return NULL;
2560 }
2561
2562 /* Construct query */
2563 query = LIBRDF_MALLOC(char*, strlen(select_contexts) + 21);
2564 if(!query) {
2565 librdf_storage_mysql_get_contexts_finished((void*)gccontext);
2566 return NULL;
2567 }
2568 sprintf(query, select_contexts, context->model);
2569
2570 /* Start query... */
2571 #ifdef LIBRDF_DEBUG_SQL
2572 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
2573 #endif
2574 if(mysql_real_query(gccontext->handle, query, strlen(query)) ||
2575 !(gccontext->results=mysql_use_result(gccontext->handle))) {
2576 librdf_log(gccontext->storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
2577 "MySQL query failed: %s",
2578 mysql_error(gccontext->handle));
2579 librdf_storage_mysql_get_contexts_finished((void*)gccontext);
2580 return NULL;
2581 }
2582 LIBRDF_FREE(char*, query);
2583
2584 /* Get first context, if any, and initialize iterator */
2585 if(librdf_storage_mysql_get_contexts_next_context(gccontext) ||
2586 !gccontext->current_context) {
2587 librdf_storage_mysql_get_contexts_finished((void*)gccontext);
2588 return librdf_new_empty_iterator(storage->world);
2589 }
2590
2591 iterator=librdf_new_iterator(storage->world,(void*)gccontext,
2592 &librdf_storage_mysql_get_contexts_end_of_iterator,
2593 &librdf_storage_mysql_get_contexts_next_context,
2594 &librdf_storage_mysql_get_contexts_get_context,
2595 &librdf_storage_mysql_get_contexts_finished);
2596 if(!iterator)
2597 librdf_storage_mysql_get_contexts_finished(gccontext);
2598 return iterator;
2599 }
2600
2601
2602 static int
librdf_storage_mysql_get_contexts_end_of_iterator(void * context)2603 librdf_storage_mysql_get_contexts_end_of_iterator(void* context)
2604 {
2605 librdf_storage_mysql_get_contexts_context* gccontext=(librdf_storage_mysql_get_contexts_context*)context;
2606
2607 return gccontext->current_context==NULL;
2608 }
2609
2610
2611 static int
librdf_storage_mysql_get_contexts_next_context(void * context)2612 librdf_storage_mysql_get_contexts_next_context(void* context)
2613 {
2614 librdf_storage_mysql_get_contexts_context* gccontext=(librdf_storage_mysql_get_contexts_context*)context;
2615 MYSQL_ROW row;
2616 librdf_node *node;
2617
2618 /* Get next statement */
2619 row=mysql_fetch_row(gccontext->results);
2620 if(row) {
2621 /* Free old context node, if allocated */
2622 if(gccontext->current_context)
2623 librdf_free_node(gccontext->current_context);
2624 /* Resource, Bnode or Literal? */
2625 if(row[0]) {
2626 if(!(node=librdf_new_node_from_uri_string(gccontext->storage->world,
2627 (const unsigned char*)row[0])))
2628 return 1;
2629 } else if(row[1]) {
2630 if(!(node=librdf_new_node_from_blank_identifier(gccontext->storage->world,
2631 (const unsigned char*)row[1])))
2632 return 1;
2633 } else if(row[2]) {
2634 /* Typed literal? */
2635 librdf_uri *datatype=NULL;
2636 if(row[4] && strlen(row[4]))
2637 datatype=librdf_new_uri(gccontext->storage->world,
2638 (const unsigned char*)row[4]);
2639 if(!(node=librdf_new_node_from_typed_literal(gccontext->storage->world,
2640 (const unsigned char*)row[2],
2641 row[3],
2642 datatype)))
2643 return 1;
2644 } else
2645 return 1;
2646
2647 gccontext->current_context=node;
2648 } else {
2649 if(gccontext->current_context)
2650 librdf_free_node(gccontext->current_context);
2651 gccontext->current_context=NULL;
2652 }
2653
2654 return 0;
2655 }
2656
2657
2658 static void*
librdf_storage_mysql_get_contexts_get_context(void * context,int flags)2659 librdf_storage_mysql_get_contexts_get_context(void* context, int flags)
2660 {
2661 librdf_storage_mysql_get_contexts_context* gccontext=(librdf_storage_mysql_get_contexts_context*)context;
2662
2663 return gccontext->current_context;
2664 }
2665
2666
2667 static void
librdf_storage_mysql_get_contexts_finished(void * context)2668 librdf_storage_mysql_get_contexts_finished(void* context)
2669 {
2670 librdf_storage_mysql_get_contexts_context* gccontext=(librdf_storage_mysql_get_contexts_context*)context;
2671
2672 if(gccontext->results)
2673 mysql_free_result(gccontext->results);
2674
2675 if(gccontext->handle) {
2676 librdf_storage_mysql_release_handle(gccontext->storage, gccontext->handle);
2677 }
2678
2679 if(gccontext->current_context)
2680 librdf_free_node(gccontext->current_context);
2681
2682 if(gccontext->storage)
2683 librdf_storage_remove_reference(gccontext->storage);
2684
2685 LIBRDF_FREE(librdf_storage_mysql_get_contexts_context, gccontext);
2686
2687 }
2688
2689
2690 /**
2691 * librdf_storage_mysql_get_feature:
2692 * @storage: #librdf_storage object
2693 * @feature: #librdf_uri feature property
2694 *
2695 * Get the value of a storage feature.
2696 *
2697 * Return value: #librdf_node feature value or NULL if no such feature
2698 * exists or the value is empty.
2699 **/
2700 static librdf_node*
librdf_storage_mysql_get_feature(librdf_storage * storage,librdf_uri * feature)2701 librdf_storage_mysql_get_feature(librdf_storage* storage, librdf_uri* feature)
2702 {
2703 unsigned char *uri_string;
2704
2705 if(!feature)
2706 return NULL;
2707
2708 uri_string=librdf_uri_as_string(feature);
2709 if(!uri_string)
2710 return NULL;
2711
2712 if(!strcmp((const char*)uri_string, (const char*)LIBRDF_MODEL_FEATURE_CONTEXTS)) {
2713 /* Always have contexts */
2714 static const unsigned char value[2]="1";
2715
2716 return librdf_new_node_from_typed_literal(storage->world,
2717 value,
2718 NULL, NULL);
2719 }
2720
2721 return NULL;
2722 }
2723
2724
2725
2726 /**
2727 * librdf_storage_mysql_transaction_start:
2728 * @storage: the storage object
2729 *
2730 * Start a transaction
2731 *
2732 * Return value: non-0 on failure
2733 **/
2734 static int
librdf_storage_mysql_transaction_start(librdf_storage * storage)2735 librdf_storage_mysql_transaction_start(librdf_storage* storage)
2736 {
2737 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance* )storage->instance;
2738 int i;
2739
2740 if(context->transaction_handle) {
2741 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
2742 "MySQL transaction already started");
2743 return 1;
2744 }
2745
2746 context->transaction_handle=librdf_storage_mysql_get_handle(storage);
2747 if(!context->transaction_handle)
2748 return 1;
2749
2750 for(i=0; i<= TABLE_STATEMENTS; i++)
2751 context->pending_inserts[i] = raptor_new_sequence((raptor_data_free_handler)free_pending_row, NULL);
2752
2753 context->pending_insert_hash_nodes=librdf_new_hash(storage->world, NULL);
2754 if(!context->pending_insert_hash_nodes)
2755 LIBRDF_FATAL1(storage->world, LIBRDF_FROM_STORAGE,
2756 "Failed to create MySQL seen nodes hash from factory");
2757
2758 if(librdf_hash_open(context->pending_insert_hash_nodes, NULL, 0, 1, 1, NULL))
2759 LIBRDF_FATAL1(storage->world, LIBRDF_FROM_STORAGE,
2760 "Failed to open MySQL seen nodes hash");
2761
2762 context->pending_statements = raptor_new_sequence((raptor_data_free_handler)free_pending_row, NULL);
2763
2764 return 0;
2765 }
2766
2767
2768 /**
2769 * librdf_storage_mysql_transaction_start_with_handle:
2770 * @storage: the storage object
2771 * @handle: the transaction object
2772 *
2773 * Start a transaction using an existing external transaction object.
2774 *
2775 * Return value: non-0 on failure
2776 **/
2777 static int
librdf_storage_mysql_transaction_start_with_handle(librdf_storage * storage,void * handle)2778 librdf_storage_mysql_transaction_start_with_handle(librdf_storage* storage,
2779 void* handle)
2780 {
2781 return librdf_storage_mysql_transaction_start(storage);
2782 }
2783
2784
2785 /*
2786 * librdf_storage_mysql_transaction_terminate:
2787 * @storage: storage object
2788 *
2789 * INTERNAL - free transaction state
2790 */
2791 static
librdf_storage_mysql_transaction_terminate(librdf_storage * storage)2792 void librdf_storage_mysql_transaction_terminate(librdf_storage *storage)
2793 {
2794 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance* )storage->instance;
2795 MYSQL* handle=context->transaction_handle;
2796 int i;
2797
2798 if(!handle)
2799 return;
2800
2801 context->transaction_handle=NULL;
2802
2803 librdf_storage_mysql_release_handle(storage, handle);
2804
2805 for(i=0; i<= TABLE_STATEMENTS; i++) {
2806 raptor_sequence* seq;
2807 seq=context->pending_inserts[i];
2808 if(seq)
2809 raptor_free_sequence(seq);
2810 context->pending_inserts[i]=NULL;
2811 }
2812
2813 if(context->pending_insert_hash_nodes) {
2814 librdf_free_hash(context->pending_insert_hash_nodes);
2815 context->pending_insert_hash_nodes=NULL;
2816 }
2817
2818 if(context->pending_statements) {
2819 raptor_free_sequence(context->pending_statements);
2820 context->pending_statements=NULL;
2821 }
2822
2823 }
2824
2825
2826 /**
2827 * librdf_storage_mysql_transaction_commit:
2828 * @storage: the storage object
2829 *
2830 * Commit a transaction.
2831 *
2832 * Return value: non-0 on failure
2833 **/
2834 static int
librdf_storage_mysql_transaction_commit(librdf_storage * storage)2835 librdf_storage_mysql_transaction_commit(librdf_storage* storage)
2836 {
2837 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance* )storage->instance;
2838 const char* query;
2839 MYSQL* handle;
2840 int status;
2841 int i;
2842 size_t query_len;
2843 const char start_query[]="START TRANSACTION";
2844 const table_info *table;
2845 raptor_stringbuffer* sb=NULL;
2846 int count=0;
2847
2848 handle=context->transaction_handle;
2849
2850 if(!handle)
2851 return 1;
2852
2853
2854 /* Take a look to see if there is anything at all to commit */
2855 count=raptor_sequence_size(context->pending_statements);
2856 for(i=0; i< TABLE_STATEMENTS; i++)
2857 count += raptor_sequence_size(context->pending_inserts[i]);
2858
2859 if(!count) {
2860 #ifdef LIBRDF_DEBUG_SQL
2861 LIBRDF_DEBUG1("Nothing pending to commit\n");
2862 #endif
2863 librdf_storage_mysql_transaction_terminate(storage);
2864 return 0;
2865 }
2866 #ifdef LIBRDF_DEBUG_SQL
2867 LIBRDF_DEBUG2("%d items pending to commit\n", count);
2868 #endif
2869
2870
2871
2872 /* START TRANSACTION */
2873 query=start_query; query_len=strlen(query);
2874
2875 #ifdef LIBRDF_DEBUG_SQL
2876 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
2877 #endif
2878 if(mysql_real_query(context->transaction_handle, query, query_len)) {
2879 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
2880 "MySQL query failed: %s",
2881 mysql_error(context->transaction_handle));
2882 librdf_storage_mysql_transaction_rollback(storage);
2883 return 1;
2884 }
2885
2886 /* INSERT node values */
2887 for(i=0; i< TABLE_STATEMENTS; i++) {
2888 raptor_sequence* seq;
2889 raptor_stringbuffer* tsb;
2890
2891 seq=context->pending_inserts[i];
2892 table=&mysql_tables[i];
2893
2894 /* sort pending nodes to always be inserted in same order */
2895 raptor_sequence_sort(seq, compare_pending_rows);
2896
2897 tsb=format_pending_row_sequence(table, seq);
2898 if(!tsb)
2899 continue;
2900
2901 query_len=raptor_stringbuffer_length(tsb);
2902 query=(char*)raptor_stringbuffer_as_string(tsb);
2903
2904 #ifdef LIBRDF_DEBUG_SQL
2905 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
2906 #endif
2907 if(mysql_real_query(context->transaction_handle, query, query_len)) {
2908 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
2909 "MySQL query to table %s failed: %s", table->name,
2910 mysql_error(context->transaction_handle));
2911 raptor_free_stringbuffer(tsb);
2912 librdf_storage_mysql_transaction_rollback(storage);
2913 return 1;
2914 }
2915
2916 raptor_free_stringbuffer(tsb);
2917 }
2918
2919
2920 /* INSERT STATEMENT* */
2921 if(raptor_sequence_size(context->pending_statements)) {
2922 char uint64_buffer[64];
2923 raptor_sequence* seq;
2924
2925 table=&mysql_tables[TABLE_STATEMENTS];
2926
2927 /* sort pending statements to always be inserted in same order */
2928 raptor_sequence_sort(context->pending_statements,
2929 compare_pending_rows);
2930
2931 sb=raptor_new_stringbuffer();
2932
2933 raptor_stringbuffer_append_string(sb, (const unsigned char*)"REPLACE INTO Statements", 1);
2934 sprintf(uint64_buffer, UINT64_T_FMT, context->model);
2935 raptor_stringbuffer_append_string(sb, (const unsigned char*)uint64_buffer, 1);
2936 raptor_stringbuffer_append_counted_string(sb, (const unsigned char*)" (", 2, 1);
2937 raptor_stringbuffer_append_string(sb, (const unsigned char*)table->columns, 1);
2938 raptor_stringbuffer_append_counted_string(sb, (const unsigned char*)") VALUES ", 9, 1);
2939
2940 seq=context->pending_statements;
2941 for(i=0; i< raptor_sequence_size(seq); i++) {
2942 pending_row* prow=(pending_row*)raptor_sequence_get_at(seq, i);
2943 int j;
2944
2945 if(i > 0)
2946 raptor_stringbuffer_append_counted_string(sb,
2947 (const unsigned char*)", ", 2, 1);
2948
2949 raptor_stringbuffer_append_counted_string(sb,
2950 (const unsigned char*)"(", 1, 1);
2951
2952 for(j=0; j < 4; j++) {
2953 if(j > 0)
2954 raptor_stringbuffer_append_counted_string(sb,
2955 (const unsigned char*)", ", 2, 1);
2956 sprintf(uint64_buffer, UINT64_T_FMT, prow->uints[j]);
2957 raptor_stringbuffer_append_string(sb,
2958 (const unsigned char*)uint64_buffer, 1);
2959 }
2960
2961 raptor_stringbuffer_append_counted_string(sb,
2962 (const unsigned char*)")", 1, 1);
2963 }
2964
2965 query=(char*)raptor_stringbuffer_as_string(sb);
2966 if(query) {
2967 #ifdef LIBRDF_DEBUG_SQL
2968 LIBRDF_DEBUG2("SQL: >>%s<<\n", query);
2969 #endif
2970 if(mysql_real_query(handle, query, strlen(query)) &&
2971 mysql_errno(handle) != ER_DUP_ENTRY) {
2972 librdf_log(storage->world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_STORAGE, NULL,
2973 "MySQL insert into %s failed with error %s",
2974 table->name, mysql_error(handle));
2975 raptor_free_stringbuffer(sb);
2976 librdf_storage_mysql_transaction_rollback(storage);
2977 return 1;
2978 }
2979 }
2980 }
2981
2982
2983 /* COMMIT */
2984 #ifdef LIBRDF_DEBUG_SQL
2985 LIBRDF_DEBUG1("SQL: mysql_commit()\n");
2986 #endif
2987 status=mysql_commit(handle);
2988
2989 librdf_storage_mysql_transaction_terminate(storage);
2990
2991 if(sb)
2992 raptor_free_stringbuffer(sb);
2993
2994 return (status != 0);
2995 }
2996
2997
2998 /**
2999 * librdf_storage_mysql_transaction_rollback:
3000 * @storage: the storage object
3001 *
3002 * Rollback a transaction.
3003 *
3004 * Return value: non-0 on failure
3005 **/
3006 static int
librdf_storage_mysql_transaction_rollback(librdf_storage * storage)3007 librdf_storage_mysql_transaction_rollback(librdf_storage* storage)
3008 {
3009 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance* )storage->instance;
3010 MYSQL* handle;
3011 int status;
3012
3013 handle=context->transaction_handle;
3014 if(!handle)
3015 return 1;
3016
3017 #ifdef LIBRDF_DEBUG_SQL
3018 LIBRDF_DEBUG1("SQL: mysql_rollback()\n");
3019 #endif
3020 status=mysql_rollback(handle);
3021
3022 librdf_storage_mysql_transaction_terminate(storage);
3023
3024 return (status != 0);
3025 }
3026
3027
3028 /**
3029 * librdf_storage_mysql_transaction_get_handle:
3030 * @storage: the storage object
3031 *
3032 * Get the current transaction handle.
3033 *
3034 * Return value: non-0 on failure
3035 **/
3036 static void*
librdf_storage_mysql_transaction_get_handle(librdf_storage * storage)3037 librdf_storage_mysql_transaction_get_handle(librdf_storage* storage)
3038 {
3039 librdf_storage_mysql_instance* context=(librdf_storage_mysql_instance* )storage->instance;
3040
3041 return context->transaction_handle;
3042 }
3043
3044
3045 /** Local entry point for dynamically loaded storage module */
3046 static void
librdf_storage_mysql_register_factory(librdf_storage_factory * factory)3047 librdf_storage_mysql_register_factory(librdf_storage_factory *factory)
3048 {
3049 LIBRDF_ASSERT_CONDITION(!strcmp(factory->name, "mysql"));
3050
3051 factory->version = LIBRDF_STORAGE_INTERFACE_VERSION;
3052 factory->init = librdf_storage_mysql_init;
3053 factory->terminate = librdf_storage_mysql_terminate;
3054 factory->open = librdf_storage_mysql_open;
3055 factory->close = librdf_storage_mysql_close;
3056 factory->sync = librdf_storage_mysql_sync;
3057 factory->size = librdf_storage_mysql_size;
3058 factory->add_statement = librdf_storage_mysql_add_statement;
3059 factory->add_statements = librdf_storage_mysql_add_statements;
3060 factory->remove_statement = librdf_storage_mysql_remove_statement;
3061 factory->contains_statement = librdf_storage_mysql_contains_statement;
3062 factory->serialise = librdf_storage_mysql_serialise;
3063 factory->find_statements = librdf_storage_mysql_find_statements;
3064 factory->find_statements_with_options = librdf_storage_mysql_find_statements_with_options;
3065 factory->context_add_statement = librdf_storage_mysql_context_add_statement;
3066 factory->context_add_statements = librdf_storage_mysql_context_add_statements;
3067 factory->context_remove_statement = librdf_storage_mysql_context_remove_statement;
3068 factory->context_remove_statements = librdf_storage_mysql_context_remove_statements;
3069 factory->context_serialise = librdf_storage_mysql_context_serialise;
3070 factory->find_statements_in_context = librdf_storage_mysql_find_statements_in_context;
3071 factory->get_contexts = librdf_storage_mysql_get_contexts;
3072 factory->get_feature = librdf_storage_mysql_get_feature;
3073
3074 factory->transaction_start = librdf_storage_mysql_transaction_start;
3075 factory->transaction_start_with_handle = librdf_storage_mysql_transaction_start_with_handle;
3076 factory->transaction_commit = librdf_storage_mysql_transaction_commit;
3077 factory->transaction_rollback = librdf_storage_mysql_transaction_rollback;
3078 factory->transaction_get_handle = librdf_storage_mysql_transaction_get_handle;
3079 }
3080
3081 #ifdef MODULAR_LIBRDF
3082
3083 /** Entry point for dynamically loaded storage module */
3084 void
librdf_storage_module_register_factory(librdf_world * world)3085 librdf_storage_module_register_factory(librdf_world *world)
3086 {
3087 librdf_storage_register_factory(world, "mysql", "MySQL database store",
3088 &librdf_storage_mysql_register_factory);
3089 }
3090
3091 #else
3092
3093 /*
3094 * librdf_init_storage_mysql:
3095 * @world: world object
3096 *
3097 * INTERNAL - Initialise the built-in storage_mysql module.
3098 */
3099 void
librdf_init_storage_mysql(librdf_world * world)3100 librdf_init_storage_mysql(librdf_world *world)
3101 {
3102 librdf_storage_register_factory(world, "mysql", "MySQL database store",
3103 &librdf_storage_mysql_register_factory);
3104 }
3105
3106 #endif
3107
3108