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