1 /* Copyright (C) 2005 MySQL AB
2 Copyright (C) 2005-2017 Alexey Kopytov <akopytov@gmail.com>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #ifdef HAVE_STRING_H
24 # include <string.h>
25 #endif
26 #ifdef HAVE_STRINGS_H
27 # include <strings.h>
28 #endif
29
30 #include <libpq-fe.h>
31
32 #include "sb_options.h"
33 #include "db_driver.h"
34 #include "sb_rand.h"
35
36 #define xfree(ptr) ({ if (ptr) free((void *)ptr); ptr = NULL; })
37
38 /* Maximum length of text representation of bind parameters */
39 #define MAX_PARAM_LENGTH 256UL
40
41 /* PostgreSQL driver arguments */
42
43 static sb_arg_t pgsql_drv_args[] =
44 {
45 SB_OPT("pgsql-host", "PostgreSQL server host", "localhost", STRING),
46 SB_OPT("pgsql-port", "PostgreSQL server port", "5432", INT),
47 SB_OPT("pgsql-user", "PostgreSQL user", "sbtest", STRING),
48 SB_OPT("pgsql-password", "PostgreSQL password", "", STRING),
49 SB_OPT("pgsql-db", "PostgreSQL database name", "sbtest", STRING),
50
51 SB_OPT_END
52 };
53
54 typedef struct
55 {
56 char *host;
57 char *port;
58 char *user;
59 char *password;
60 char *db;
61 } pgsql_drv_args_t;
62
63 /* Structure used for DB-to-PgSQL bind types map */
64
65 typedef struct
66 {
67 db_bind_type_t db_type;
68 int pg_type;
69 } db_pgsql_bind_map_t;
70
71 /* DB-to-PgSQL bind types map */
72 db_pgsql_bind_map_t db_pgsql_bind_map[] =
73 {
74 {DB_TYPE_TINYINT, 0},
75 {DB_TYPE_SMALLINT, 21},
76 {DB_TYPE_INT, 23},
77 {DB_TYPE_BIGINT, 20},
78 {DB_TYPE_FLOAT, 700},
79 {DB_TYPE_DOUBLE, 701},
80 {DB_TYPE_DATETIME, 0},
81 {DB_TYPE_TIMESTAMP, 1114},
82 {DB_TYPE_CHAR, 18},
83 {DB_TYPE_VARCHAR, 1043},
84 {DB_TYPE_NONE, 0}
85 };
86
87 /* PgSQL driver capabilities */
88
89 static drv_caps_t pgsql_drv_caps =
90 {
91 1, /* multi_rows_insert */
92 1, /* prepared_statements */
93 0, /* auto_increment */
94 0, /* needs_commit */
95 1, /* serial */
96 0, /* unsigned int */
97 };
98
99 /* Describes the PostgreSQL prepared statement */
100 typedef struct pg_stmt
101 {
102 char *name;
103 int prepared;
104 int nparams;
105 Oid *ptypes;
106 char **pvalues;
107 } pg_stmt_t;
108
109 static pgsql_drv_args_t args; /* driver args */
110
111 static char use_ps; /* whether server-side prepared statemens should be used */
112
113 /* PgSQL driver operations */
114
115 static int pgsql_drv_init(void);
116 static int pgsql_drv_describe(drv_caps_t *);
117 static int pgsql_drv_connect(db_conn_t *);
118 static int pgsql_drv_disconnect(db_conn_t *);
119 static int pgsql_drv_prepare(db_stmt_t *, const char *, size_t);
120 static int pgsql_drv_bind_param(db_stmt_t *, db_bind_t *, size_t);
121 static int pgsql_drv_bind_result(db_stmt_t *, db_bind_t *, size_t);
122 static db_error_t pgsql_drv_execute(db_stmt_t *, db_result_t *);
123 static int pgsql_drv_fetch(db_result_t *);
124 static int pgsql_drv_fetch_row(db_result_t *, db_row_t *);
125 static db_error_t pgsql_drv_query(db_conn_t *, const char *, size_t,
126 db_result_t *);
127 static int pgsql_drv_free_results(db_result_t *);
128 static int pgsql_drv_close(db_stmt_t *);
129 static int pgsql_drv_done(void);
130
131 /* PgSQL driver definition */
132
133 static db_driver_t pgsql_driver =
134 {
135 .sname = "pgsql",
136 .lname = "PostgreSQL driver",
137 .args = pgsql_drv_args,
138 .ops =
139 {
140 .init = pgsql_drv_init,
141 .describe = pgsql_drv_describe,
142 .connect = pgsql_drv_connect,
143 .disconnect = pgsql_drv_disconnect,
144 .prepare = pgsql_drv_prepare,
145 .bind_param = pgsql_drv_bind_param,
146 .bind_result = pgsql_drv_bind_result,
147 .execute = pgsql_drv_execute,
148 .fetch = pgsql_drv_fetch,
149 .fetch_row = pgsql_drv_fetch_row,
150 .free_results = pgsql_drv_free_results,
151 .close = pgsql_drv_close,
152 .query = pgsql_drv_query,
153 .done = pgsql_drv_done
154 }
155 };
156
157
158 /* Local functions */
159
160 static int get_pgsql_bind_type(db_bind_type_t);
161 static int get_unique_stmt_name(char *, int);
162
163 /* Register PgSQL driver */
164
165
register_driver_pgsql(sb_list_t * drivers)166 int register_driver_pgsql(sb_list_t *drivers)
167 {
168 SB_LIST_ADD_TAIL(&pgsql_driver.listitem, drivers);
169
170 return 0;
171 }
172
173 /* PgSQL driver initialization */
174
pgsql_drv_init(void)175 int pgsql_drv_init(void)
176 {
177 args.host = sb_get_value_string("pgsql-host");
178 args.port = sb_get_value_string("pgsql-port");
179 args.user = sb_get_value_string("pgsql-user");
180 args.password = sb_get_value_string("pgsql-password");
181 args.db = sb_get_value_string("pgsql-db");
182
183 use_ps = 0;
184 pgsql_drv_caps.prepared_statements = 1;
185 if (db_globals.ps_mode != DB_PS_MODE_DISABLE)
186 use_ps = 1;
187
188 return 0;
189 }
190
191
192 /* Describe database capabilities */
193
194
pgsql_drv_describe(drv_caps_t * caps)195 int pgsql_drv_describe(drv_caps_t *caps)
196 {
197 PGconn *con;
198
199 *caps = pgsql_drv_caps;
200
201 /* Determine the server version */
202 con = PQsetdbLogin(args.host,
203 args.port,
204 NULL,
205 NULL,
206 args.db,
207 args.user,
208 args.password);
209 if (PQstatus(con) != CONNECTION_OK)
210 {
211 log_text(LOG_FATAL, "Connection to database failed: %s",
212 PQerrorMessage(con));
213 PQfinish(con);
214 return 1;
215 }
216
217 /* Support for multi-row INSERTs is not available before 8.2 */
218 if (PQserverVersion(con) < 80200)
219 caps->multi_rows_insert = 0;
220
221 PQfinish(con);
222
223 return 0;
224 }
225
empty_notice_processor(void * arg,const char * msg)226 static void empty_notice_processor(void *arg, const char *msg)
227 {
228 (void) arg; /* unused */
229 (void) msg; /* unused */
230 }
231
232 /* Connect to database */
233
pgsql_drv_connect(db_conn_t * sb_conn)234 int pgsql_drv_connect(db_conn_t *sb_conn)
235 {
236 PGconn *con;
237
238 con = PQsetdbLogin(args.host,
239 args.port,
240 NULL,
241 NULL,
242 args.db,
243 args.user,
244 args.password);
245 if (PQstatus(con) != CONNECTION_OK)
246 {
247 log_text(LOG_FATAL, "Connection to database failed: %s",
248 PQerrorMessage(con));
249 PQfinish(con);
250 return 1;
251 }
252
253 /* Silence the default notice receiver spitting NOTICE message to stderr */
254 PQsetNoticeProcessor(con, empty_notice_processor, NULL);
255 sb_conn->ptr = con;
256
257 return 0;
258 }
259
260 /* Disconnect from database */
261
pgsql_drv_disconnect(db_conn_t * sb_conn)262 int pgsql_drv_disconnect(db_conn_t *sb_conn)
263 {
264 PGconn *con = (PGconn *)sb_conn->ptr;
265
266 /* These might be allocated in pgsql_check_status() */
267 xfree(sb_conn->sql_state);
268 xfree(sb_conn->sql_errmsg);
269
270 if (con != NULL)
271 PQfinish(con);
272
273 return 0;
274 }
275
276
277 /* Prepare statement */
278
279
pgsql_drv_prepare(db_stmt_t * stmt,const char * query,size_t len)280 int pgsql_drv_prepare(db_stmt_t *stmt, const char *query, size_t len)
281 {
282 PGconn *con = (PGconn *)stmt->connection->ptr;
283 PGresult *pgres;
284 pg_stmt_t *pgstmt;
285 char *buf = NULL;
286 unsigned int vcnt;
287 unsigned int need_realloc;
288 unsigned int i,j;
289 unsigned int buflen;
290 int n;
291 char name[32];
292
293 (void) len; /* unused */
294
295 if (con == NULL)
296 return 1;
297
298 if (!use_ps)
299 {
300 /* Use client-side PS */
301 stmt->emulated = 1;
302 stmt->query = strdup(query);
303
304 return 0;
305 }
306
307 /* Convert query to PgSQL-style named placeholders */
308 need_realloc = 1;
309 vcnt = 1;
310 buflen = 0;
311 for (i = 0, j = 0; query[i] != '\0'; i++)
312 {
313 again:
314 if (j+1 >= buflen || need_realloc)
315 {
316 buflen = (buflen > 0) ? buflen * 2 : 256;
317 buf = realloc(buf, buflen);
318 if (buf == NULL)
319 goto error;
320 need_realloc = 0;
321 }
322
323 if (query[i] != '?')
324 {
325 buf[j++] = query[i];
326 continue;
327 }
328
329 n = snprintf(buf + j, buflen - j, "$%d", vcnt);
330 if (n < 0 || n >= (int)(buflen - j))
331 {
332 need_realloc = 1;
333 goto again;
334 }
335
336 j += n;
337 vcnt++;
338 }
339 buf[j] = '\0';
340
341 /* Store the query to be prepared later on the first bind_param call */
342 stmt->query = strdup(buf);
343 free(buf);
344
345 pgstmt = (pg_stmt_t *)calloc(1, sizeof(pg_stmt_t));
346 if (pgstmt == NULL)
347 goto error;
348 /* Generate random statement name */
349 get_unique_stmt_name(name, sizeof(name));
350 pgstmt->name = strdup(name);
351 pgstmt->nparams = vcnt - 1;
352
353 /*
354 Special keys for statements without parameters, since we don't need
355 to know the types of arguments, and no calls to bind_param() will be made
356 */
357 if (pgstmt->nparams == 0)
358 {
359 /* Do prepare */
360 pgres = PQprepare(con, pgstmt->name, stmt->query, pgstmt->nparams,
361 NULL);
362
363 if (PQresultStatus(pgres) != PGRES_COMMAND_OK)
364 {
365 log_text(LOG_FATAL, "PQprepare() failed: %s", PQerrorMessage(con));
366
367 PQclear(pgres);
368
369 free(stmt->query);
370 free(pgstmt->name);
371 free(pgstmt);
372
373 return 1;
374 }
375 PQclear(pgres);
376 pgstmt->prepared = 1;
377 }
378
379 stmt->ptr = pgstmt;
380
381 return 0;
382
383 error:
384
385 return 1;
386 }
387
388
389 /* Bind parameters for prepared statement */
390
391
pgsql_drv_bind_param(db_stmt_t * stmt,db_bind_t * params,size_t len)392 int pgsql_drv_bind_param(db_stmt_t *stmt, db_bind_t *params, size_t len)
393 {
394 PGconn *con = (PGconn *)stmt->connection->ptr;
395 PGresult *pgres;
396 pg_stmt_t *pgstmt;
397 unsigned int i;
398
399 if (con == NULL)
400 return 1;
401
402 if (stmt->bound_param != NULL)
403 free(stmt->bound_param);
404 stmt->bound_param = (db_bind_t *)malloc(len * sizeof(db_bind_t));
405 if (stmt->bound_param == NULL)
406 return 1;
407 memcpy(stmt->bound_param, params, len * sizeof(db_bind_t));
408 stmt->bound_param_len = len;
409
410 if (stmt->emulated)
411 return 0;
412
413 pgstmt = stmt->ptr;
414 if (pgstmt->prepared)
415 return 0;
416
417 /* Prepare statement here, since we need to know types of parameters */
418 /* Validate parameters count */
419 if ((unsigned)pgstmt->nparams != len)
420 {
421 log_text(LOG_ALERT, "wrong number of parameters in prepared statement");
422 log_text(LOG_DEBUG, "counted: %d, passed to bind_param(): %zd",
423 pgstmt->nparams, len);
424 return 1;
425 }
426
427 pgstmt->ptypes = (Oid *)malloc(len * sizeof(int));
428 if (pgstmt->ptypes == NULL)
429 return 1;
430
431 /* Convert sysbench data types to PgSQL ones */
432 for (i = 0; i < len; i++)
433 pgstmt->ptypes[i] = get_pgsql_bind_type(params[i].type);
434
435 /* Do prepare */
436 pgres = PQprepare(con, pgstmt->name, stmt->query, pgstmt->nparams,
437 pgstmt->ptypes);
438
439 if (PQresultStatus(pgres) != PGRES_COMMAND_OK)
440 {
441 log_text(LOG_FATAL, "PQprepare() failed: %s", PQerrorMessage(con));
442 return 1;
443 }
444
445 PQclear(pgres);
446
447 pgstmt->pvalues = (char **)calloc(len, sizeof(char *));
448 if (pgstmt->pvalues == NULL)
449 return 1;
450
451 /* Allocate buffers for bind parameters */
452 for (i = 0; i < len; i++)
453 {
454 if (pgstmt->pvalues[i] != NULL)
455 {
456 free(pgstmt->pvalues[i]);
457 }
458
459 pgstmt->pvalues[i] = (char *)malloc(MAX_PARAM_LENGTH);
460 if (pgstmt->pvalues[i] == NULL)
461 return 1;
462 }
463 pgstmt->prepared = 1;
464
465 return 0;
466 }
467
468
469 /* Bind results for prepared statement */
470
471
pgsql_drv_bind_result(db_stmt_t * stmt,db_bind_t * params,size_t len)472 int pgsql_drv_bind_result(db_stmt_t *stmt, db_bind_t *params, size_t len)
473 {
474 /* unused */
475 (void)stmt;
476 (void)params;
477 (void)len;
478
479 return 0;
480 }
481
482
483 /* Check query execution status */
484
485
pgsql_check_status(db_conn_t * con,PGresult * pgres,const char * funcname,const char * query,db_result_t * rs)486 static db_error_t pgsql_check_status(db_conn_t *con, PGresult *pgres,
487 const char *funcname, const char *query,
488 db_result_t *rs)
489 {
490 ExecStatusType status;
491 db_error_t rc;
492 PGconn * const pgcon = con->ptr;
493
494 status = PQresultStatus(pgres);
495 switch(status) {
496 case PGRES_TUPLES_OK:
497 rs->nrows = PQntuples(pgres);
498 rs->nfields = PQnfields(pgres);
499 rs->counter = SB_CNT_READ;
500
501 rc = DB_ERROR_NONE;
502
503 break;
504
505 case PGRES_COMMAND_OK:
506 rs->nrows = strtoul(PQcmdTuples(pgres), NULL, 10);;
507 rs->counter = (rs->nrows > 0) ? SB_CNT_WRITE : SB_CNT_OTHER;
508 rc = DB_ERROR_NONE;
509
510 /*
511 Since we are not returning a result set, the SQL layer will never call
512 pgsql_drv_free_results(). So we must call PQclear() here.
513 */
514 PQclear(pgres);
515
516 break;
517
518 case PGRES_FATAL_ERROR:
519 rs->nrows = 0;
520 rs->counter = SB_CNT_ERROR;
521
522 /*
523 Duplicate strings here, because PostgreSQL will deallocate them on
524 PQclear() call below. They will be deallocated either on subsequent calls
525 to pgsql_check_status() or in pgsql_drv_disconnect().
526 */
527 xfree(con->sql_state);
528 xfree(con->sql_errmsg);
529
530 con->sql_state = strdup(PQresultErrorField(pgres, PG_DIAG_SQLSTATE));
531 con->sql_errmsg = strdup(PQresultErrorField(pgres, PG_DIAG_MESSAGE_PRIMARY));
532
533 if (!strcmp(con->sql_state, "40P01") /* deadlock_detected */ ||
534 !strcmp(con->sql_state, "23505") /* unique violation */ ||
535 !strcmp(con->sql_state, "40001"))/* serialization_failure */
536 {
537 PGresult *tmp;
538 tmp = PQexec(pgcon, "ROLLBACK");
539 PQclear(tmp);
540 rc = DB_ERROR_IGNORABLE;
541 }
542 else
543 {
544 log_text(LOG_FATAL, "%s() failed: %d %s", funcname, status,
545 con->sql_errmsg);
546
547 if (query != NULL)
548 log_text(LOG_FATAL, "failed query was: %s", query);
549
550 rc = DB_ERROR_FATAL;
551 }
552
553 PQclear(pgres);
554
555 break;
556
557 default:
558 rs->nrows = 0;
559 rs->counter = SB_CNT_ERROR;
560 rc = DB_ERROR_FATAL;
561 }
562
563 return rc;
564 }
565
566
567 /* Execute prepared statement */
568
569
pgsql_drv_execute(db_stmt_t * stmt,db_result_t * rs)570 db_error_t pgsql_drv_execute(db_stmt_t *stmt, db_result_t *rs)
571 {
572 db_conn_t *con = stmt->connection;
573 PGconn *pgcon = (PGconn *)con->ptr;
574 PGresult *pgres;
575 pg_stmt_t *pgstmt;
576 char *buf = NULL;
577 unsigned int buflen = 0;
578 unsigned int i, j, vcnt;
579 char need_realloc;
580 int n;
581 db_error_t rc;
582 unsigned long len;
583
584 con->sql_errno = 0;
585 xfree(con->sql_state);
586 xfree(con->sql_errmsg);
587
588 if (!stmt->emulated)
589 {
590 pgstmt = stmt->ptr;
591 if (pgstmt == NULL)
592 {
593 log_text(LOG_DEBUG,
594 "ERROR: exiting mysql_drv_execute(), uninitialized statement");
595 return DB_ERROR_FATAL;
596 }
597
598 /* Convert sysbench bind structures to PgSQL data */
599 for (i = 0; i < (unsigned)pgstmt->nparams; i++)
600 {
601 if (stmt->bound_param[i].is_null && *(stmt->bound_param[i].is_null))
602 continue;
603
604 switch (stmt->bound_param[i].type) {
605 case DB_TYPE_CHAR:
606 case DB_TYPE_VARCHAR:
607
608 len = stmt->bound_param[i].data_len[0];
609
610 memcpy(pgstmt->pvalues[i], stmt->bound_param[i].buffer,
611 SB_MIN(MAX_PARAM_LENGTH, len));
612 /* PostgreSQL requires a zero-terminated string */
613 pgstmt->pvalues[i][len] = '\0';
614
615 break;
616 default:
617 db_print_value(stmt->bound_param + i, pgstmt->pvalues[i],
618 MAX_PARAM_LENGTH);
619 }
620 }
621
622 pgres = PQexecPrepared(pgcon, pgstmt->name, pgstmt->nparams,
623 (const char **)pgstmt->pvalues, NULL, NULL, 1);
624
625 rc = pgsql_check_status(con, pgres, "PQexecPrepared", NULL, rs);
626
627 rs->ptr = (rs->counter == SB_CNT_READ) ? (void *) pgres : NULL;
628
629 return rc;
630 }
631
632 /* Use emulation */
633 /* Build the actual query string from parameters list */
634 need_realloc = 1;
635 vcnt = 0;
636 for (i = 0, j = 0; stmt->query[i] != '\0'; i++)
637 {
638 again:
639 if (j+1 >= buflen || need_realloc)
640 {
641 buflen = (buflen > 0) ? buflen * 2 : 256;
642 buf = realloc(buf, buflen);
643 if (buf == NULL)
644 return DB_ERROR_FATAL;
645 need_realloc = 0;
646 }
647
648 if (stmt->query[i] != '?')
649 {
650 buf[j++] = stmt->query[i];
651 continue;
652 }
653
654 n = db_print_value(stmt->bound_param + vcnt, buf + j, buflen - j);
655 if (n < 0)
656 {
657 need_realloc = 1;
658 goto again;
659 }
660 j += n;
661 vcnt++;
662 }
663 buf[j] = '\0';
664
665 rc = pgsql_drv_query(con, buf, j, rs);
666
667 free(buf);
668
669 return rc;
670 }
671
672
673 /* Execute SQL query */
674
675
pgsql_drv_query(db_conn_t * sb_conn,const char * query,size_t len,db_result_t * rs)676 db_error_t pgsql_drv_query(db_conn_t *sb_conn, const char *query, size_t len,
677 db_result_t *rs)
678 {
679 PGconn *pgcon = sb_conn->ptr;
680 PGresult *pgres;
681 db_error_t rc;
682
683 (void)len; /* unused */
684
685 sb_conn->sql_errno = 0;
686 xfree(sb_conn->sql_state);
687 xfree(sb_conn->sql_errmsg);
688
689 pgres = PQexec(pgcon, query);
690 rc = pgsql_check_status(sb_conn, pgres, "PQexec", query, rs);
691
692 rs->ptr = (rs->counter == SB_CNT_READ) ? (void *) pgres : NULL;
693
694 return rc;
695 }
696
697
698 /* Fetch row from result set of a prepared statement */
699
700
pgsql_drv_fetch(db_result_t * rs)701 int pgsql_drv_fetch(db_result_t *rs)
702 {
703 /* NYI */
704 (void)rs;
705
706 return 1;
707 }
708
709
710 /* Fetch row from result set of a query */
711
712
pgsql_drv_fetch_row(db_result_t * rs,db_row_t * row)713 int pgsql_drv_fetch_row(db_result_t *rs, db_row_t *row)
714 {
715 intptr_t rownum;
716 int i;
717
718 /*
719 Use row->ptr as a row number, rather than a pointer to avoid dynamic
720 memory management.
721 */
722 rownum = (intptr_t) row->ptr;
723 if (rownum >= (int) rs->nrows)
724 return DB_ERROR_IGNORABLE;
725
726 for (i = 0; i < (int) rs->nfields; i++)
727 {
728 /*
729 PQgetvalue() returns an empty string, not a NULL value for a NULL
730 field. Callers of this function expect a NULL pointer in this case.
731 */
732 if (PQgetisnull(rs->ptr, rownum, i))
733 row->values[i].ptr = NULL;
734 else
735 {
736 row->values[i].len = PQgetlength(rs->ptr, rownum, i);
737 row->values[i].ptr = PQgetvalue(rs->ptr, rownum, i);
738 }
739 }
740
741 row->ptr = (void *) (rownum + 1);
742
743 return DB_ERROR_NONE;
744 }
745
746
747 /* Free result set */
748
749
pgsql_drv_free_results(db_result_t * rs)750 int pgsql_drv_free_results(db_result_t *rs)
751 {
752 if (rs->ptr != NULL)
753 {
754 PQclear((PGresult *)rs->ptr);
755 rs->ptr = NULL;
756
757 rs->row.ptr = 0;
758 return 0;
759 }
760
761 return 1;
762 }
763
764
765 /* Close prepared statement */
766
767
pgsql_drv_close(db_stmt_t * stmt)768 int pgsql_drv_close(db_stmt_t *stmt)
769 {
770 pg_stmt_t *pgstmt = stmt->ptr;
771 int i;
772
773 if (pgstmt == NULL)
774 return 1;
775
776 if (pgstmt->name != NULL)
777 free(pgstmt->name);
778 if (pgstmt->ptypes != NULL)
779 free(pgstmt->ptypes);
780 if (pgstmt->pvalues != NULL)
781 {
782 for (i = 0; i < pgstmt->nparams; i++)
783 if (pgstmt->pvalues[i] != NULL)
784 free(pgstmt->pvalues[i]);
785 free(pgstmt->pvalues);
786 }
787
788 xfree(stmt->ptr);
789
790 return 0;
791 }
792
793
794 /* Uninitialize driver */
pgsql_drv_done(void)795 int pgsql_drv_done(void)
796 {
797 return 0;
798 }
799
800
801 /* Map SQL data type to bind_type value in MYSQL_BIND */
802
803
get_pgsql_bind_type(db_bind_type_t type)804 int get_pgsql_bind_type(db_bind_type_t type)
805 {
806 unsigned int i;
807
808 for (i = 0; db_pgsql_bind_map[i].db_type != DB_TYPE_NONE; i++)
809 if (db_pgsql_bind_map[i].db_type == type)
810 return db_pgsql_bind_map[i].pg_type;
811
812 return -1;
813 }
814
815
get_unique_stmt_name(char * name,int len)816 int get_unique_stmt_name(char *name, int len)
817 {
818 return snprintf(name, len, "sbstmt%d%d",
819 (int) sb_rand_uniform_uint64(),
820 (int) sb_rand_uniform_uint64());
821 }
822