1 /* Copyright (C) 2009 Sun Microsystems, Inc.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2 of the License, or
6    (at your option) any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21 
22 #ifdef HAVE_STRING_H
23 # include <string.h>
24 #endif
25 #ifdef HAVE_STRINGS_H
26 # include <strings.h>
27 #endif
28 #include <stdio.h>
29 
30 #include <libdrizzle/drizzle_client.h>
31 
32 #include "sb_options.h"
33 
34 #include "db_driver.h"
35 
36 #define DEBUG(format, ...) do { if (db_globals.debug) log_text(LOG_DEBUG, format, __VA_ARGS__); } while (0)
37 
38 /* Drizzle driver arguments */
39 
40 static sb_arg_t drizzle_drv_args[] =
41 {
42   {"drizzle-host", "Drizzle server host", SB_ARG_TYPE_LIST, "localhost"},
43   {"drizzle-port", "Drizzle server port", SB_ARG_TYPE_INT, "4427"},
44   {"drizzle-socket", "Drizzle socket", SB_ARG_TYPE_STRING, NULL},
45   {"drizzle-user", "Drizzle user", SB_ARG_TYPE_STRING, ""},
46   {"drizzle-password", "Drizzle password", SB_ARG_TYPE_STRING, ""},
47   {"drizzle-db", "Drizzle database name", SB_ARG_TYPE_STRING, "sbtest"},
48   {"drizzle-buffer", "Level of library buffering (none, field, row, all)", SB_ARG_TYPE_STRING, "none"},
49   {"drizzle-mysql", "Use MySQL Protocol", SB_ARG_TYPE_BOOL, "off"},
50 
51   {NULL, NULL, SB_ARG_TYPE_NULL, NULL}
52 };
53 
54 typedef enum
55 {
56   BUFFER_NONE,
57   BUFFER_FIELD,
58   BUFFER_ROW,
59   BUFFER_ALL
60 } buffer_level;
61 
62 typedef struct
63 {
64   sb_list_t          *hosts;
65   unsigned int       port;
66   char               *socket;
67   char               *user;
68   char               *password;
69   char               *db;
70   buffer_level       buffer;
71   int                mysql;
72 } drizzle_drv_args_t;
73 
74 /* Drizzle driver capabilities */
75 
76 static drv_caps_t drizzle_drv_caps =
77 {
78   .multi_rows_insert = 1,
79   .prepared_statements = 0,
80   .auto_increment = 1,
81   .serial = 0,
82   .unsigned_int = 0,
83 };
84 
85 static drizzle_drv_args_t args;          /* driver args */
86 
87 static sb_list_item_t *hosts_pos;
88 
89 static pthread_mutex_t hosts_mutex;
90 
91 static void column_info(drizzle_column_st *column);
92 
93 /* Drizzle driver operations */
94 
95 static int drizzle_drv_init(void);
96 static int drizzle_drv_describe(drv_caps_t *);
97 static int drizzle_drv_connect(db_conn_t *);
98 static int drizzle_drv_disconnect(db_conn_t *);
99 static int drizzle_drv_prepare(db_stmt_t *, const char *, size_t);
100 static int drizzle_drv_bind_param(db_stmt_t *, db_bind_t *, size_t);
101 static int drizzle_drv_bind_result(db_stmt_t *, db_bind_t *, size_t);
102 static db_error_t drizzle_drv_execute(db_stmt_t *, db_result_t *);
103 static int drizzle_drv_fetch(db_result_t *);
104 static int drizzle_drv_fetch_row(db_result_t *, db_row_t *);
105 static db_error_t drizzle_drv_query(db_conn_t *, const char *, size_t,
106                              db_result_t *);
107 static int drizzle_drv_free_results(db_result_t *);
108 static int drizzle_drv_close(db_stmt_t *);
109 static int drizzle_drv_store_results(db_result_t *);
110 static int drizzle_drv_done(void);
111 
112 /* Drizzle driver definition */
113 
114 static db_driver_t drizzle_driver =
115 {
116   .sname = "drizzle",
117   .lname = "Drizzle driver",
118   .args = drizzle_drv_args,
119   .ops =
120   {
121     .init = drizzle_drv_init,
122     .describe = drizzle_drv_describe,
123     .connect = drizzle_drv_connect,
124     .disconnect = drizzle_drv_disconnect,
125     .prepare = drizzle_drv_prepare,
126     .bind_param = drizzle_drv_bind_param,
127     .bind_result = drizzle_drv_bind_result,
128     .execute = drizzle_drv_execute,
129     .fetch = drizzle_drv_fetch,
130     .fetch_row = drizzle_drv_fetch_row,
131     .free_results = drizzle_drv_free_results,
132     .close = drizzle_drv_close,
133     .query = drizzle_drv_query,
134     .store_results = drizzle_drv_store_results,
135     .done = drizzle_drv_done
136   },
137 };
138 
139 
140 /* Local functions */
141 
142 /* Register Drizzle driver */
143 
144 
register_driver_drizzle(sb_list_t * drivers)145 int register_driver_drizzle(sb_list_t *drivers)
146 {
147   SB_LIST_ADD_TAIL(&drizzle_driver.listitem, drivers);
148 
149   return 0;
150 }
151 
152 
153 /* Drizzle driver initialization */
154 
155 
drizzle_drv_init(void)156 int drizzle_drv_init(void)
157 {
158   char *s;
159 
160   args.hosts = sb_get_value_list("drizzle-host");
161   if (SB_LIST_IS_EMPTY(args.hosts))
162   {
163     log_text(LOG_FATAL, "No Drizzle hosts specified, aborting");
164     return 1;
165   }
166   hosts_pos = args.hosts;
167   pthread_mutex_init(&hosts_mutex, NULL);
168 
169   args.port = (unsigned int)sb_get_value_int("drizzle-port");
170   args.socket = sb_get_value_string("drizzle-socket");
171   args.user = sb_get_value_string("drizzle-user");
172   args.password = sb_get_value_string("drizzle-password");
173   args.db = sb_get_value_string("drizzle-db");
174   s= sb_get_value_string("drizzle-buffer");
175   if (!strcasecmp(s, "none"))
176     args.buffer= BUFFER_NONE;
177   else if (!strcasecmp(s, "field"))
178     args.buffer= BUFFER_FIELD;
179   else if (!strcasecmp(s, "row"))
180     args.buffer= BUFFER_ROW;
181   else if (!strcasecmp(s, "all"))
182     args.buffer= BUFFER_ALL;
183 
184   args.mysql= sb_get_value_flag("drizzle-mysql");
185 
186   return 0;
187 }
188 
189 
190 /* Describe database capabilities (possibly depending on table type) */
191 
192 
drizzle_drv_describe(drv_caps_t * caps)193 int drizzle_drv_describe(drv_caps_t *caps )
194 {
195   *caps = drizzle_drv_caps;
196 
197   return 0;
198 }
199 
200 
201 /* Connect to Drizzle database */
202 
203 
drizzle_drv_connect(db_conn_t * sb_conn)204 int drizzle_drv_connect(db_conn_t *sb_conn)
205 {
206   drizzle_st              *drizzle_lib= NULL;
207   drizzle_con_st          *con= NULL;
208   const char              *host;
209   drizzle_return_t        ret;
210 
211   drizzle_lib= drizzle_create(drizzle_lib);
212 
213   if (args.socket)
214   {
215     DEBUG("drizzle_con_add_uds(%p, %p \"%s\", \"%s\", \"%s\", \"%s\", %d)",
216       drizzle_lib,
217       con,
218       args.socket,
219       args.user,
220       args.password,
221       args.db,
222       args.mysql ? DRIZZLE_CON_MYSQL : 0);
223     con= drizzle_con_add_uds(drizzle_lib,
224                              con,
225                              args.socket,
226                              args.user,
227                              args.password,
228                              args.db,
229                              args.mysql ? DRIZZLE_CON_MYSQL : 0);
230   } else {
231 
232     pthread_mutex_lock(&hosts_mutex);
233     hosts_pos = SB_LIST_ITEM_NEXT(hosts_pos);
234     if (hosts_pos == args.hosts)
235       hosts_pos = SB_LIST_ITEM_NEXT(hosts_pos);
236     host = SB_LIST_ENTRY(hosts_pos, value_t, listitem)->data;
237     pthread_mutex_unlock(&hosts_mutex);
238 
239     DEBUG("drizzle_con_add_tcp(%p, %p \"%s\", %u, \"%s\", \"%s\", \"%s\", %d)",
240           drizzle_lib,
241           con,
242           host,
243           args.port,
244           args.user,
245           args.password,
246           args.db,
247           args.mysql ? DRIZZLE_CON_MYSQL : 0);
248     con= drizzle_con_add_tcp(drizzle_lib,
249                              con,
250                              host,
251                              args.port,
252                              args.user,
253                              args.password,
254                              args.db,
255                              args.mysql ? DRIZZLE_CON_MYSQL : 0);
256   }
257 
258   if (con == NULL)
259   {
260     log_text(LOG_FATAL, "unable to Add Drizzle Connection, aborting...");
261     log_text(LOG_FATAL, "error %d: %s", drizzle_errno(drizzle_lib),
262              drizzle_error(drizzle_lib));
263     return 1;
264   }
265   if ((ret= drizzle_con_connect(con)) != DRIZZLE_RETURN_OK)
266   {
267     log_text(LOG_FATAL, "unable to connect to Drizzle server: %d", ret);
268     log_text(LOG_FATAL, "error %d: %s", drizzle_errno(drizzle_lib),
269              drizzle_error(drizzle_lib));
270     free(con);
271     return 1;
272 
273   }
274   sb_conn->ptr = con;
275 
276   return 0;
277 }
278 
279 
280 /* Disconnect from Drizzle database */
281 
282 
drizzle_drv_disconnect(db_conn_t * sb_conn)283 int drizzle_drv_disconnect(db_conn_t *sb_conn)
284 {
285   drizzle_con_st *con = (drizzle_con_st *)sb_conn->ptr;
286 
287   if (con != NULL)
288   {
289     DEBUG("drizzle_close(%p)", con);
290     drizzle_con_close(con);
291     free(con);
292   }
293   return 0;
294 }
295 
296 
297 /* Prepare statement */
298 
299 
drizzle_drv_prepare(db_stmt_t * stmt,const char * query,size_t len)300 int drizzle_drv_prepare(db_stmt_t *stmt, const char *query, size_t len)
301 {
302 
303   (void) len; /* unused */
304 
305   /* Use client-side PS */
306   stmt->emulated = 1;
307   stmt->query = strdup(query);
308 
309   return 0;
310 }
311 
312 
313 /* Bind parameters for prepared statement */
drizzle_drv_bind_param(db_stmt_t * stmt,db_bind_t * params,size_t len)314 int drizzle_drv_bind_param(db_stmt_t *stmt, db_bind_t *params, size_t len)
315 {
316   drizzle_con_st        *con = (drizzle_con_st *)stmt->connection->ptr;
317 
318   if (con == NULL)
319     return 1;
320 
321   /* Use emulation */
322   if (stmt->bound_param != NULL)
323     free(stmt->bound_param);
324   stmt->bound_param = (db_bind_t *)malloc(len * sizeof(db_bind_t));
325   if (stmt->bound_param == NULL)
326     return 1;
327   memcpy(stmt->bound_param, params, len * sizeof(db_bind_t));
328   stmt->bound_param_len = len;
329 
330   return 0;
331 
332 }
333 
334 
335 /* Bind results for prepared statement */
drizzle_drv_bind_result(db_stmt_t * stmt,db_bind_t * params,size_t len)336 int drizzle_drv_bind_result(db_stmt_t *stmt, db_bind_t *params, size_t len)
337 {
338   (void)stmt;
339   (void)params;
340   (void)len;
341   return 0;
342 }
343 
344 
345 /* Execute prepared statement */
346 
347 
drizzle_drv_execute(db_stmt_t * stmt,db_result_t * rs)348 db_error_t drizzle_drv_execute(db_stmt_t *stmt, db_result_t *rs)
349 {
350   db_conn_t       *con = stmt->connection;
351   char            *buf = NULL;
352   unsigned int    buflen = 0;
353   unsigned int    i, j, vcnt;
354   char            need_realloc;
355   int             n;
356 
357   /* Use emulation */
358   /* Build the actual query string from parameters list */
359   need_realloc = 1;
360   vcnt = 0;
361   for (i = 0, j = 0; stmt->query[i] != '\0'; i++)
362   {
363   again:
364     if (j+1 >= buflen || need_realloc)
365     {
366       buflen = (buflen > 0) ? buflen * 2 : 256;
367       buf = realloc(buf, buflen);
368       if (buf == NULL)
369       {
370         log_text(LOG_DEBUG, "ERROR: exiting drizzle_drv_execute(), memory allocation failure");
371         return DB_ERROR_FATAL;
372       }
373       need_realloc = 0;
374     }
375 
376     if (stmt->query[i] != '?')
377     {
378       buf[j++] = stmt->query[i];
379       continue;
380     }
381 
382     n = db_print_value(stmt->bound_param + vcnt, buf + j, (int)(buflen - j));
383     if (n < 0)
384     {
385       need_realloc = 1;
386       goto again;
387     }
388     j += (unsigned int)n;
389     vcnt++;
390   }
391   buf[j] = '\0';
392 
393   con->error = drizzle_drv_query(con, buf, j, rs);
394   free(buf);
395   if (con->error != DB_ERROR_NONE)
396   {
397     log_text(LOG_DEBUG, "ERROR: exiting drizzle_drv_execute(), database error");
398     return con->error;
399   }
400 
401   return DB_ERROR_NONE;
402 }
403 
404 
405 /* Execute SQL query */
406 
407 
drizzle_drv_query(db_conn_t * sb_conn,const char * query,size_t len,db_result_t * rs)408 db_error_t drizzle_drv_query(db_conn_t *sb_conn, const char *query, size_t len,
409                              db_result_t *rs)
410 {
411   drizzle_con_st *con = sb_conn->ptr;
412   unsigned int rc;
413   drizzle_return_t ret;
414   drizzle_result_st *result= NULL;
415 
416   DEBUG("drizzle_query(%p, %p, \"%s\", %u, %p)",
417         con,
418         result,
419         query,
420         len,
421         &ret);
422   result= drizzle_query(con, NULL, query, len, &ret);
423   DEBUG("drizzle_query(%p) == %d", con, ret);
424 
425   if (ret == DRIZZLE_RETURN_ERROR_CODE)
426   {
427     rc= drizzle_result_error_code(result);
428     /* Error code constants haven't been added yet to libdrizzle
429        ER_LOCK_DEADLOCK==1213
430        ER_LOCK_WAIT_TIMEOUT==1205
431        ER_CHECKREAD==1020
432      */
433     if (rc == 1213 || rc == 1205 ||
434         rc == 1020)
435       return DB_ERROR_IGNORABLE;
436     log_text(LOG_ALERT, "Drizzle Query Failed: %u:%s",
437              drizzle_result_error_code(result),
438              drizzle_result_error(result));
439     return DB_ERROR_FATAL;
440   }
441   else if (ret != DRIZZLE_RETURN_OK)
442   {
443     rc = drizzle_con_errno(con);
444     DEBUG("drizzle_errno(%p) = %u", drizzle_con_drizzle(con), rc);
445     log_text(LOG_ALERT, "failed to execute Drizzle query: len==%d `%s`:",
446              strlen(query), query);
447     log_text(LOG_ALERT, "Error %d %s", drizzle_con_errno(con),
448              drizzle_con_error(con));
449     return DB_ERROR_FATAL;
450   }
451   DEBUG("drizzle_query \"%s\" returned %d", query, ret);
452 
453   if (result == NULL)
454   {
455     DEBUG("drizzle_query(%p, \"%s\") == NULL",con,query);
456     return DB_ERROR_FATAL;
457   }
458 
459 
460   rs->ptr= result;
461   rs->nrows= drizzle_result_row_count(result);
462   DEBUG("drizzle_result_row_count(%p) == %d",result,rs->nrows);
463 
464   return DB_ERROR_NONE;
465 }
466 
467 
468 /* Fetch row from result set of a prepared statement */
469 
470 
drizzle_drv_fetch(db_result_t * rs)471 int drizzle_drv_fetch(db_result_t *rs)
472 {
473   /* NYI */
474   (void)rs;
475   printf("in drizzle_drv_fetch_row!\n");
476 
477   return 1;
478 }
479 
480 
481 /* Fetch row from result set of a query */
482 
483 
drizzle_drv_fetch_row(db_result_t * rs,db_row_t * row)484 int drizzle_drv_fetch_row(db_result_t *rs, db_row_t *row)
485 {
486   /* NYI */
487   printf("in drizzle_drv_fetch_row!\n");
488   (void)rs;  /* unused */
489   (void)row; /* unused */
490 
491   return 1;
492 }
493 
494 
495 /* Store results from the last query */
496 
497 
drizzle_drv_store_results(db_result_t * rs)498 int drizzle_drv_store_results(db_result_t *rs)
499 {
500 
501   drizzle_con_st        *con = rs->connection->ptr;
502   drizzle_result_st     *res = rs->ptr;
503   drizzle_return_t      ret;
504   drizzle_column_st     *column= NULL;
505   unsigned int rc;
506 
507 
508   if (con == NULL || res == NULL)
509     return DB_ERROR_FATAL;
510 
511 
512   if (args.buffer == BUFFER_ALL)
513   {
514 
515     ret= drizzle_result_buffer(res);
516     DEBUG("drizzle_result_buffer(%p) = %d", res, ret);
517     if (ret != DRIZZLE_RETURN_OK)
518     {
519       rc = drizzle_con_errno(con);
520       DEBUG("drizzle_errno(%p) = %u", drizzle_con_drizzle(con), rc);
521       log_text(LOG_ALERT, "drizzle_result_buffer failed: `%p`:", res);
522       log_text(LOG_ALERT, "Error %d %s",
523                drizzle_con_errno(con),
524                drizzle_con_error(con));
525       return DB_ERROR_FATAL;
526     }
527     while ((column = drizzle_column_next(res)) != NULL)
528       column_info(column);
529   }
530   else if (drizzle_result_column_count(res) > 0)
531   {
532 
533     drizzle_row_t         row;
534     drizzle_field_t       field;
535     uint64_t              row_num;
536     size_t offset= 0;
537     size_t length;
538     size_t total;
539 
540     /* Read column meta-info */
541     while (1)
542     {
543         column= drizzle_column_read(res, column, &ret);
544         DEBUG("drizzle_column_read(%p,%p,%p) == %d",res, column, &ret, ret);
545         if (ret != DRIZZLE_RETURN_OK)
546         {
547           rc = drizzle_con_errno(con);
548           DEBUG("drizzle_errno(%p) = %u", drizzle_con_drizzle(con), rc);
549           log_text(LOG_ALERT, "drizzle_column_read failed: `%p`:", res);
550           log_text(LOG_ALERT, "Error %d %s",
551                    drizzle_con_errno(con),
552                    drizzle_con_error(con));
553           return DB_ERROR_FATAL;
554         }
555         if (column == NULL)
556           break;
557 
558         column_info(column);
559         drizzle_column_free(column);
560     }
561 
562     /* Actually fetch rows */
563     while (1) /* Loop for rows */
564     {
565       if (args.buffer == BUFFER_ROW)
566       {
567         row= drizzle_row_buffer(res, &ret);
568         DEBUG("drizzle_row_buffer(%p, %p) == %p, %d",res, &ret, row, ret);
569         if (ret != DRIZZLE_RETURN_OK)
570         {
571           rc = drizzle_con_errno(con);
572           DEBUG("drizzle_errno(%p) = %u", drizzle_con_drizzle(con), rc);
573           log_text(LOG_ALERT, "drizzle_row_buffer failed: `%p`:", res);
574           log_text(LOG_ALERT, "Error %d %s",
575                    drizzle_con_errno(con),
576                    drizzle_con_error(con));
577           return DB_ERROR_FATAL;
578         }
579 
580         if (row == NULL)
581           break;
582 
583         DEBUG("drizzle_row_free(%p, %p)",res,row);
584         drizzle_row_free(res, row);
585       }
586       else if (args.buffer == BUFFER_NONE || args.buffer == BUFFER_FIELD)
587       {
588         row_num= drizzle_row_read(res, &ret);
589         DEBUG("drizzle_row_read(%p, %p) == %"PRIu64", %d",
590               res, &ret, row_num, ret);
591         if (ret != DRIZZLE_RETURN_OK)
592         {
593           rc = drizzle_con_errno(con);
594           DEBUG("drizzle_errno(%p) = %u", drizzle_con_drizzle(con), rc);
595           log_text(LOG_ALERT, "drizzle_row_read failed: `%p`:", res);
596           log_text(LOG_ALERT, "Error %d %s",
597                    drizzle_con_errno(con),
598                    drizzle_con_error(con));
599           return DB_ERROR_FATAL;
600         }
601 
602         if (row_num == 0)
603           break;
604 
605         while (1) /* Loop for fields */
606         {
607 
608           if (args.buffer == BUFFER_FIELD)
609           {
610             /* Since an entire field is buffered, we don't need to worry about
611                partial reads. */
612             field= drizzle_field_buffer(res, &total, &ret);
613             DEBUG("drizzle_field_buffer(%p, &p, %p) == %p, %x, %d",
614                   res, &total, &ret, field, total, ret);
615             length= total;
616           }
617           else
618           {
619             field= drizzle_field_read(res, &offset, &length, &total, &ret);
620             DEBUG("drizzle_field_read(%p, %p, %p, %p, %p) == "
621                   "%p, %x, %x, %x, %d",
622                   res, &offset, &length, &total, &ret,
623                   field, offset, length, total, ret);
624           }
625 
626           if (ret == DRIZZLE_RETURN_ROW_END)
627             break;
628           else if (ret != DRIZZLE_RETURN_OK)
629           {
630             rc = drizzle_con_errno(con);
631             DEBUG("drizzle_errno(%p) = %u", drizzle_con_drizzle(con), rc);
632             log_text(LOG_ALERT, "drizzle_field_(buffer|read) failed: `%p`:",
633                      res);
634             log_text(LOG_ALERT, "Error %d %s",
635                      drizzle_con_errno(con),
636                      drizzle_con_error(con));
637             return DB_ERROR_FATAL;
638           }
639 
640           if (args.buffer == BUFFER_FIELD)
641             drizzle_field_free(field);
642 
643         } /* while (1) Loop for fields */
644 
645       } /* if (args.buffer) */
646 
647     } /* while (1) */
648   }
649   return DB_ERROR_NONE;
650 }
651 
652 
653 /* Free result set */
654 
655 
drizzle_drv_free_results(db_result_t * rs)656 int drizzle_drv_free_results(db_result_t *rs)
657 {
658 
659   if (rs->ptr != NULL)
660   {
661     DEBUG("drizzle_result_free(%p)", rs->ptr);
662     drizzle_result_free(rs->ptr);
663     rs->ptr = NULL;
664     return 0;
665   }
666 
667   return 1;
668 }
669 
670 
671 /* Close prepared statement */
672 
673 
drizzle_drv_close(db_stmt_t * stmt)674 int drizzle_drv_close(db_stmt_t *stmt)
675 {
676   (void)stmt;
677   return 0;
678 }
679 
680 
681 /* Uninitialize driver */
drizzle_drv_done(void)682 int drizzle_drv_done(void)
683 {
684   return 0;
685 }
686 
687 
column_info(drizzle_column_st * column)688 void column_info(drizzle_column_st *column)
689 {
690   DEBUG("Field:   catalog=%s\n"
691          "              db=%s\n"
692          "           table=%s\n"
693          "       org_table=%s\n"
694          "            name=%s\n"
695          "        org_name=%s\n"
696          "         charset=%u\n"
697          "            size=%u\n"
698          "            type=%u\n"
699          "           flags=%u\n",
700          drizzle_column_catalog(column), drizzle_column_db(column),
701          drizzle_column_table(column), drizzle_column_orig_table(column),
702          drizzle_column_name(column), drizzle_column_orig_name(column),
703          drizzle_column_charset(column), drizzle_column_size(column),
704          drizzle_column_type(column), drizzle_column_flags(column));
705 }
706 
707