1 /* Copyright 2014 Hewlett-Packard Development Company, L.P.
2    based on the Drizzle driver:
3    Copyright (C) 2009 Sun Microsystems, Inc.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23 
24 #ifdef HAVE_STRING_H
25 # include <string.h>
26 #endif
27 #ifdef HAVE_STRINGS_H
28 # include <strings.h>
29 #endif
30 #include <stdio.h>
31 
32 #include <stdint.h>
33 #include <libattachsql-1.0/attachsql.h>
34 
35 #include "sb_options.h"
36 
37 #include "db_driver.h"
38 
39 #define DEBUG(format, ...) do { if (db_globals.debug) log_text(LOG_DEBUG, format, __VA_ARGS__); } while (0)
40 
41 /* Drizzle driver arguments */
42 
43 static sb_arg_t attachsql_drv_args[] =
44 {
45   {"attachsql-host", "libAttachSQL server host", SB_ARG_TYPE_LIST, "localhost"},
46   {"attachsql-port", "libAttachSQL server port", SB_ARG_TYPE_INT, "4427"},
47   {"attachsql-socket", "libAttachSQL socket", SB_ARG_TYPE_STRING, NULL},
48   {"attachsql-user", "libAttachSQL user", SB_ARG_TYPE_STRING, ""},
49   {"attachsql-password", "libAttachSQL password", SB_ARG_TYPE_STRING, ""},
50   {"attachsql-db", "libAttachSQL database name", SB_ARG_TYPE_STRING, "sbtest"},
51   {NULL, NULL, SB_ARG_TYPE_NULL, NULL}
52 };
53 
54 typedef struct
55 {
56   sb_list_t          *hosts;
57   unsigned int       port;
58   char               *socket;
59   char               *user;
60   char               *password;
61   char               *db;
62 } attachsql_drv_args_t;
63 
64 /* AttachSQL driver capabilities
65  * At a later date we will add prepared statements to this
66  */
67 
68 static drv_caps_t attachsql_drv_caps =
69 {
70   .multi_rows_insert = 1,
71   .prepared_statements = 1,
72   .auto_increment = 1,
73   .serial = 0,
74   .unsigned_int = 0,
75 };
76 
77 static attachsql_drv_args_t args;          /* driver args */
78 
79 static sb_list_item_t *hosts_pos;
80 
81 static pthread_mutex_t hosts_mutex;
82 
83 /* libAttachSQL driver operations */
84 
85 static int attachsql_drv_init(void);
86 static int attachsql_drv_describe(drv_caps_t *);
87 static int attachsql_drv_connect(db_conn_t *);
88 static int attachsql_drv_disconnect(db_conn_t *);
89 static int attachsql_drv_prepare(db_stmt_t *, const char *, size_t);
90 static int attachsql_drv_bind_param(db_stmt_t *, db_bind_t *, size_t);
91 static int attachsql_drv_bind_result(db_stmt_t *, db_bind_t *, size_t);
92 static db_error_t attachsql_drv_execute(db_stmt_t *, db_result_t *);
93 static int attachsql_drv_fetch(db_result_t *);
94 static int attachsql_drv_fetch_row(db_result_t *, db_row_t *);
95 static db_error_t attachsql_drv_query(db_conn_t *, const char *, size_t,
96                                       db_result_t *);
97 static int attachsql_drv_free_results(db_result_t *);
98 static int attachsql_drv_close(db_stmt_t *);
99 static int attachsql_drv_store_results(db_result_t *);
100 static int attachsql_drv_done(void);
101 
102 /* libAttachSQL driver definition */
103 
104 static db_driver_t attachsql_driver =
105 {
106   .sname = "attachsql",
107   .lname = "libAttachSQL driver",
108   .args = attachsql_drv_args,
109   .ops =
110   {
111     .init = attachsql_drv_init,
112     .describe = attachsql_drv_describe,
113     .connect = attachsql_drv_connect,
114     .disconnect = attachsql_drv_disconnect,
115     .prepare = attachsql_drv_prepare,
116     .bind_param = attachsql_drv_bind_param,
117     .bind_result = attachsql_drv_bind_result,
118     .execute = attachsql_drv_execute,
119     .fetch = attachsql_drv_fetch,
120     .fetch_row = attachsql_drv_fetch_row,
121     .free_results = attachsql_drv_free_results,
122     .close = attachsql_drv_close,
123     .query = attachsql_drv_query,
124     .store_results = attachsql_drv_store_results,
125     .done = attachsql_drv_done
126   }
127 };
128 
129 
130 /* Local functions */
131 
132 /* Register libAttachSQL driver */
133 
134 
register_driver_attachsql(sb_list_t * drivers)135 int register_driver_attachsql(sb_list_t *drivers)
136 {
137   SB_LIST_ADD_TAIL(&attachsql_driver.listitem, drivers);
138 
139   return 0;
140 }
141 
142 
143 /* libAttachSQL driver initialization */
144 
145 
attachsql_drv_init(void)146 int attachsql_drv_init(void)
147 {
148   args.hosts = sb_get_value_list("attachsql-host");
149   if (SB_LIST_IS_EMPTY(args.hosts))
150   {
151     log_text(LOG_FATAL, "No libAttachSQL hosts specified, aborting");
152     return 1;
153   }
154   hosts_pos = args.hosts;
155   pthread_mutex_init(&hosts_mutex, NULL);
156 
157   args.port = (unsigned int)sb_get_value_int("attachsql-port");
158   args.socket = sb_get_value_string("attachsql-socket");
159   args.user = sb_get_value_string("attachsql-user");
160   args.password = sb_get_value_string("attachsql-password");
161   args.db = sb_get_value_string("attachsql-db");
162   attachsql_library_init();
163   return 0;
164 }
165 
166 
167 /* Describe database capabilities (possibly depending on table type) */
168 
169 
attachsql_drv_describe(drv_caps_t * caps)170 int attachsql_drv_describe(drv_caps_t *caps )
171 {
172   *caps = attachsql_drv_caps;
173 
174   return 0;
175 }
176 
177 
178 /* Connect to libAttachSQL database */
179 
180 
attachsql_drv_connect(db_conn_t * sb_conn)181 int attachsql_drv_connect(db_conn_t *sb_conn)
182 {
183   attachsql_connect_t     *con= NULL;
184   const char              *host;
185   attachsql_error_t      *error= NULL;
186   attachsql_return_t aret= ATTACHSQL_RETURN_NONE;
187 
188   if (args.socket)
189   {
190     DEBUG("attachsql_connect_create(\"%s\", \"%s\", \"%s\", \"%s\")",
191       args.socket,
192       args.user,
193       args.password,
194       args.db);
195     con= attachsql_connect_create(args.socket,
196                              0,
197                              args.user,
198                              args.password,
199                              args.db,
200                              &error);
201   } else {
202 
203     pthread_mutex_lock(&hosts_mutex);
204     hosts_pos = SB_LIST_ITEM_NEXT(hosts_pos);
205     if (hosts_pos == args.hosts)
206       hosts_pos = SB_LIST_ITEM_NEXT(hosts_pos);
207     host = SB_LIST_ENTRY(hosts_pos, value_t, listitem)->data;
208     pthread_mutex_unlock(&hosts_mutex);
209 
210     DEBUG("attachsql_connect_create(\"%s\", %u, \"%s\", \"%s\", \"%s\")",
211           host,
212           args.port,
213           args.user,
214           args.password,
215           args.db);
216     con= attachsql_connect_create(host,
217                              args.port,
218                              args.user,
219                              args.password,
220                              args.db,
221                              &error);
222   }
223   if (con == NULL)
224   {
225     log_text(LOG_FATAL, "unable to Add libAttachSQL Connection, aborting...");
226     log_text(LOG_FATAL, "error %d: %s", attachsql_error_code(error), attachsql_error_message(error));
227     attachsql_error_free(error);
228     return 1;
229   }
230   attachsql_connect_set_option(con, ATTACHSQL_OPTION_SEMI_BLOCKING, NULL);
231 
232   if (!attachsql_connect(con, &error))
233   {
234     log_text(LOG_FATAL, "unable to connect to libAttachSQL server");
235     log_text(LOG_FATAL, "error %d: %s", attachsql_error_code(error), attachsql_error_message(error));
236     attachsql_error_free(error);
237     attachsql_connect_destroy(con);
238     return 1;
239 
240   }
241 
242   while (aret != ATTACHSQL_RETURN_IDLE)
243   {
244     aret = attachsql_connect_poll(con, &error);
245 
246     if (error)
247     {
248       log_text(LOG_FATAL, "unable to connect to libAttachSQL server");
249       log_text(LOG_FATAL, "error %d: %s", attachsql_error_code(error), attachsql_error_message(error));
250       attachsql_error_free(error);
251       attachsql_connect_destroy(con);
252       return 1;
253     }
254   }
255 
256   sb_conn->ptr = con;
257 
258   return 0;
259 }
260 
261 
262 /* Disconnect from libAttachSQL database */
263 
264 
attachsql_drv_disconnect(db_conn_t * sb_conn)265 int attachsql_drv_disconnect(db_conn_t *sb_conn)
266 {
267   attachsql_connect_t *con = (attachsql_connect_t *)sb_conn->ptr;
268 
269   if (con != NULL)
270   {
271     DEBUG("attachsql_connect_destroy(%p)", con);
272     attachsql_connect_destroy(con);
273   }
274   return 0;
275 }
276 
277 
278 /* Prepare statement */
279 
280 
attachsql_drv_prepare(db_stmt_t * stmt,const char * query,size_t len)281 int attachsql_drv_prepare(db_stmt_t *stmt, const char *query, size_t len)
282 {
283   attachsql_connect_t *con= (attachsql_connect_t *)stmt->connection->ptr;
284   attachsql_error_t *error= NULL;
285   attachsql_return_t aret= ATTACHSQL_RETURN_NONE;
286   attachsql_statement_prepare(con, len, query, &error);
287   while(aret != ATTACHSQL_RETURN_EOF)
288   {
289     aret= attachsql_connect_poll(con, &error);
290     if (error)
291     {
292       log_text(LOG_ALERT, "libAttachSQL Prepare Failed: %u:%s", attachsql_error_code(error), attachsql_error_message(error));
293       attachsql_error_free(error);
294       return DB_ERROR_FATAL;
295     }
296   }
297 
298   return 0;
299 }
300 
301 
302 /* Bind parameters for prepared statement */
attachsql_drv_bind_param(db_stmt_t * stmt,db_bind_t * params,size_t len)303 int attachsql_drv_bind_param(db_stmt_t *stmt, db_bind_t *params, size_t len)
304 {
305   /* libAttachSQL doesn't do this, you do this during execute
306    * this is because sysbench doesn't set the values until that time
307    */
308 
309   if (stmt->bound_param != NULL)
310     free(stmt->bound_param);
311   stmt->bound_param = (db_bind_t *)malloc(len * sizeof(db_bind_t));
312   if (stmt->bound_param == NULL)
313     return 1;
314   memcpy(stmt->bound_param, params, len * sizeof(db_bind_t));
315   stmt->bound_param_len = len;
316 
317   return 0;
318 
319 }
320 
321 
322 /* Bind results for prepared statement */
attachsql_drv_bind_result(db_stmt_t * stmt,db_bind_t * params,size_t len)323 int attachsql_drv_bind_result(db_stmt_t *stmt, db_bind_t *params, size_t len)
324 {
325   (void)stmt;
326   (void)params;
327   (void)len;
328   /* libAttachSQL doesn't do this, you get after execute */
329   return 0;
330 }
331 
332 
333 /* Execute prepared statement */
334 
335 
attachsql_drv_execute(db_stmt_t * stmt,db_result_t * rs)336 db_error_t attachsql_drv_execute(db_stmt_t *stmt, db_result_t *rs)
337 {
338   (void) rs;
339   attachsql_connect_t *con= (attachsql_connect_t *)stmt->connection->ptr;
340   attachsql_return_t aret= ATTACHSQL_RETURN_NONE;
341   attachsql_error_t *error= NULL;
342 
343   uint16_t i;
344   int8_t tinyint;
345   int16_t smallint;
346   int32_t normalint;
347   int64_t bigint;
348   float float_type;
349   double double_type;
350   db_time_t *time_data;
351   if (con == NULL)
352     return 1;
353 
354   for (i= 0; i < stmt->bound_param_len; i++)
355   {
356     db_bind_t *param= &stmt->bound_param[i];
357     switch(param->type)
358     {
359       case DB_TYPE_TINYINT:
360         tinyint= *(int8_t*)param->buffer;
361         attachsql_statement_set_int(con, i, tinyint, NULL);
362         break;
363       case DB_TYPE_SMALLINT:
364         smallint= *(int16_t*)param->buffer;
365         attachsql_statement_set_int(con, i, smallint, NULL);
366         break;
367       case DB_TYPE_INT:
368         normalint= *(int32_t*)param->buffer;
369         attachsql_statement_set_int(con, i, normalint, NULL);
370         break;
371       case DB_TYPE_BIGINT:
372         bigint= *(int64_t*)param->buffer;
373         attachsql_statement_set_bigint(con, i, bigint, NULL);
374         break;
375       case DB_TYPE_FLOAT:
376         float_type= *(float*)param->buffer;
377         attachsql_statement_set_float(con, i, float_type, NULL);
378         break;
379       case DB_TYPE_DOUBLE:
380         double_type= *(double*)param->buffer;
381         attachsql_statement_set_double(con, i, double_type, NULL);
382         break;
383       case DB_TYPE_TIME:
384         time_data= (db_time_t*)param->buffer;
385         attachsql_statement_set_time(con, i, time_data->hour, time_data->minute, time_data->second, 0, false, NULL);
386         break;
387       case DB_TYPE_DATE:
388       case DB_TYPE_DATETIME:
389       case DB_TYPE_TIMESTAMP:
390         time_data= (db_time_t*)param->buffer;
391         attachsql_statement_set_datetime(con, i, time_data->year, time_data->month, time_data->day, time_data->hour, time_data->minute, time_data->second, 0, NULL);
392         break;
393       case DB_TYPE_CHAR:
394       case DB_TYPE_VARCHAR:
395         attachsql_statement_set_string(con, i, param->max_len, param->buffer, NULL);
396       case DB_TYPE_NONE:
397       default:
398         attachsql_statement_set_null(con, i, NULL);
399         /* Not supported */
400     }
401   }
402 
403   attachsql_statement_execute(con, &error);
404 
405   while(aret != ATTACHSQL_RETURN_EOF)
406   {
407     aret= attachsql_connect_poll(con, &error);
408     if (aret == ATTACHSQL_RETURN_ROW_READY)
409     {
410       return 0;
411     }
412     if (error)
413     {
414       log_text(LOG_ALERT, "libAttachSQL Execute Failed: %u:%s", attachsql_error_code(error), attachsql_error_message(error));
415       attachsql_error_free(error);
416       return DB_ERROR_FATAL;
417     }
418   }
419 
420   return DB_ERROR_NONE;
421 }
422 
423 
424 /* Execute SQL query */
425 
426 
attachsql_drv_query(db_conn_t * sb_conn,const char * query,size_t len,db_result_t * rs)427 db_error_t attachsql_drv_query(db_conn_t *sb_conn, const char *query,
428                                size_t len, db_result_t *rs)
429 {
430   (void) rs;
431   attachsql_connect_t *con = sb_conn->ptr;
432   unsigned int rc;
433   attachsql_error_t *error= NULL;
434   attachsql_return_t aret= ATTACHSQL_RETURN_NONE;
435 
436   /* Close any previous query */
437   attachsql_query_close(con);
438 
439   DEBUG("attachsql_query(%p, \"%s\", %u)",
440         con,
441         query,
442         len);
443   attachsql_query(con, len, query, 0, NULL, &error);
444 
445   while((aret != ATTACHSQL_RETURN_EOF) && (aret != ATTACHSQL_RETURN_ROW_READY))
446   {
447     aret= attachsql_connect_poll(con, &error);
448 
449     if (error)
450     {
451       rc= attachsql_error_code(error);
452       if (rc == 1213 || rc == 1205 || rc == 1020)
453       {
454         attachsql_error_free(error);
455         return DB_ERROR_IGNORABLE;
456       }
457       log_text(LOG_ALERT, "libAttachSQL Query Failed: %u:%s", attachsql_error_code(error), attachsql_error_message(error));
458       attachsql_error_free(error);
459       return DB_ERROR_FATAL;
460     }
461   }
462   //rs->connection->ptr= con;
463   DEBUG("attachsql_query \"%s\" returned %d", query, aret);
464 
465   return DB_ERROR_NONE;
466 }
467 
468 
469 /* Fetch row from result set of a prepared statement */
470 
471 
attachsql_drv_fetch(db_result_t * rs)472 int attachsql_drv_fetch(db_result_t *rs)
473 {
474   /* NYI */
475   attachsql_connect_t *con = rs->connection->ptr;
476   size_t tmp_len;
477   uint16_t columns, col;
478   attachsql_return_t aret= ATTACHSQL_RETURN_NONE;
479   attachsql_error_t *error= NULL;
480 
481   while((aret != ATTACHSQL_RETURN_EOF) && (aret != ATTACHSQL_RETURN_ROW_READY))
482   {
483     aret= attachsql_connect_poll(con, &error);
484 
485     if (error)
486     {
487       log_text(LOG_ALERT, "libAttachSQL Query Failed: %u:%s", attachsql_error_code(error), attachsql_error_message(error));
488       attachsql_error_free(error);
489       return 1;
490     }
491   }
492   if (aret == ATTACHSQL_RETURN_EOF)
493   {
494     return 1;
495   }
496   attachsql_statement_row_get(con, NULL);
497   columns= attachsql_query_column_count(con);
498   for (col= 0; col < columns; col++)
499   {
500     switch (attachsql_statement_get_column_type(con, col))
501     {
502       case ATTACHSQL_COLUMN_TYPE_TINY:
503       case ATTACHSQL_COLUMN_TYPE_SHORT:
504       case ATTACHSQL_COLUMN_TYPE_LONG:
505       case ATTACHSQL_COLUMN_TYPE_YEAR:
506       case ATTACHSQL_COLUMN_TYPE_INT24:
507         attachsql_statement_get_int(con, col, &error);
508         break;
509       case ATTACHSQL_COLUMN_TYPE_LONGLONG:
510         attachsql_statement_get_bigint(con, col, &error);
511         break;
512       case ATTACHSQL_COLUMN_TYPE_FLOAT:
513         attachsql_statement_get_float(con, col, &error);
514         break;
515       case ATTACHSQL_COLUMN_TYPE_DOUBLE:
516         attachsql_statement_get_double(con, col, &error);
517         break;
518       default:
519         attachsql_statement_get_char(con, col, &tmp_len, &error);
520         break;
521     }
522   }
523   attachsql_query_row_next(con);
524 
525   return 0;
526 }
527 
528 
529 /* Fetch row from result set of a query */
530 
531 
attachsql_drv_fetch_row(db_result_t * rs,db_row_t * row)532 int attachsql_drv_fetch_row(db_result_t *rs, db_row_t *row)
533 {
534   attachsql_error_t *error= NULL;
535   attachsql_return_t aret= ATTACHSQL_RETURN_NONE;
536 
537   /* NYI */
538 
539   attachsql_connect_t *con = rs->connection->ptr;
540 
541   while((aret != ATTACHSQL_RETURN_EOF) && (aret != ATTACHSQL_RETURN_ROW_READY))
542   {
543     aret= attachsql_connect_poll(con, &error);
544 
545     if (error)
546     {
547       log_text(LOG_ALERT, "libAttachSQL Query Failed: %u:%s", attachsql_error_code(error), attachsql_error_message(error));
548       attachsql_error_free(error);
549       return 1;
550     }
551   }
552   if (aret == ATTACHSQL_RETURN_EOF)
553   {
554     return 1;
555   }
556   row->ptr= attachsql_query_row_get(con, NULL);
557   attachsql_query_row_next(con);
558 
559   return 0;
560 }
561 
562 
563 /* Store results from the last query */
564 
565 
attachsql_drv_store_results(db_result_t * rs)566 int attachsql_drv_store_results(db_result_t *rs)
567 {
568   int ret= 0;
569   db_row_t row;
570   /* libAttachSQL can't do things in this order */
571   while (ret == 0)
572   {
573     if (rs->statement != NULL)
574     {
575       ret= attachsql_drv_fetch(rs);
576     }
577     else
578     {
579       ret= attachsql_drv_fetch_row(rs, &row);
580     }
581   }
582 
583   return DB_ERROR_NONE;
584 }
585 
586 
587 /* Free result set */
588 
589 
attachsql_drv_free_results(db_result_t * rs)590 int attachsql_drv_free_results(db_result_t *rs)
591 {
592 
593   if (rs->connection->ptr != NULL)
594   {
595     DEBUG("attachsql_query_close(%p)", rs->connection->ptr);
596     attachsql_query_close(rs->connection->ptr);
597     rs->connection->ptr = NULL;
598     return 0;
599   }
600 
601   return 1;
602 }
603 
604 
605 /* Close prepared statement */
606 
607 
attachsql_drv_close(db_stmt_t * stmt)608 int attachsql_drv_close(db_stmt_t *stmt)
609 {
610   attachsql_connect_t *con= (attachsql_connect_t *)stmt->connection->ptr;
611   attachsql_statement_close(con);
612 
613   return 0;
614 }
615 
616 
617 /* Uninitialize driver */
attachsql_drv_done(void)618 int attachsql_drv_done(void)
619 {
620   return 0;
621 }
622 
623