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