apr_dbd_pgsql.c revision 251876
1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "apu.h" 18 19#if APU_HAVE_PGSQL 20 21#include "apu_config.h" 22 23#include <ctype.h> 24#include <stdlib.h> 25 26#ifdef HAVE_LIBPQ_FE_H 27#include <libpq-fe.h> 28#elif defined(HAVE_POSTGRESQL_LIBPQ_FE_H) 29#include <postgresql/libpq-fe.h> 30#endif 31 32#include "apr_strings.h" 33#include "apr_time.h" 34#include "apr_buckets.h" 35 36#include "apr_dbd_internal.h" 37 38struct apr_dbd_transaction_t { 39 int mode; 40 int errnum; 41 apr_dbd_t *handle; 42}; 43 44struct apr_dbd_t { 45 PGconn *conn; 46 apr_dbd_transaction_t *trans; 47}; 48 49struct apr_dbd_results_t { 50 int random; 51 PGconn *handle; 52 PGresult *res; 53 size_t ntuples; 54 size_t sz; 55 size_t index; 56 apr_pool_t *pool; 57}; 58 59struct apr_dbd_row_t { 60 int n; 61 apr_dbd_results_t *res; 62}; 63 64struct apr_dbd_prepared_t { 65 const char *name; 66 int prepared; 67 int nargs; 68 int nvals; 69 apr_dbd_type_e *types; 70}; 71 72#define dbd_pgsql_is_success(x) (((x) == PGRES_EMPTY_QUERY) \ 73 || ((x) == PGRES_COMMAND_OK) \ 74 || ((x) == PGRES_TUPLES_OK)) 75 76static apr_status_t clear_result(void *data) 77{ 78 PQclear(data); 79 return APR_SUCCESS; 80} 81 82static int dbd_pgsql_select(apr_pool_t *pool, apr_dbd_t *sql, 83 apr_dbd_results_t **results, 84 const char *query, int seek) 85{ 86 PGresult *res; 87 int ret; 88 if ( sql->trans && sql->trans->errnum ) { 89 return sql->trans->errnum; 90 } 91 if (seek) { /* synchronous query */ 92 if (TXN_IGNORE_ERRORS(sql->trans)) { 93 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP"); 94 if (res) { 95 int ret = PQresultStatus(res); 96 PQclear(res); 97 if (!dbd_pgsql_is_success(ret)) { 98 sql->trans->errnum = ret; 99 return PGRES_FATAL_ERROR; 100 } 101 } else { 102 return sql->trans->errnum = PGRES_FATAL_ERROR; 103 } 104 } 105 res = PQexec(sql->conn, query); 106 if (res) { 107 ret = PQresultStatus(res); 108 if (dbd_pgsql_is_success(ret)) { 109 ret = 0; 110 } else { 111 PQclear(res); 112 } 113 } else { 114 ret = PGRES_FATAL_ERROR; 115 } 116 if (ret != 0) { 117 if (TXN_IGNORE_ERRORS(sql->trans)) { 118 PGresult *res = PQexec(sql->conn, 119 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP"); 120 if (res) { 121 int ret = PQresultStatus(res); 122 PQclear(res); 123 if (!dbd_pgsql_is_success(ret)) { 124 sql->trans->errnum = ret; 125 return PGRES_FATAL_ERROR; 126 } 127 } else { 128 return sql->trans->errnum = PGRES_FATAL_ERROR; 129 } 130 } else if (TXN_NOTICE_ERRORS(sql->trans)){ 131 sql->trans->errnum = ret; 132 } 133 return ret; 134 } else { 135 if (TXN_IGNORE_ERRORS(sql->trans)) { 136 PGresult *res = PQexec(sql->conn, 137 "RELEASE SAVEPOINT APR_DBD_TXN_SP"); 138 if (res) { 139 int ret = PQresultStatus(res); 140 PQclear(res); 141 if (!dbd_pgsql_is_success(ret)) { 142 sql->trans->errnum = ret; 143 return PGRES_FATAL_ERROR; 144 } 145 } else { 146 return sql->trans->errnum = PGRES_FATAL_ERROR; 147 } 148 } 149 } 150 if (!*results) { 151 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t)); 152 } 153 (*results)->res = res; 154 (*results)->ntuples = PQntuples(res); 155 (*results)->sz = PQnfields(res); 156 (*results)->random = seek; 157 (*results)->pool = pool; 158 apr_pool_cleanup_register(pool, res, clear_result, 159 apr_pool_cleanup_null); 160 } 161 else { 162 if (TXN_IGNORE_ERRORS(sql->trans)) { 163 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP"); 164 if (res) { 165 int ret = PQresultStatus(res); 166 PQclear(res); 167 if (!dbd_pgsql_is_success(ret)) { 168 sql->trans->errnum = ret; 169 return PGRES_FATAL_ERROR; 170 } 171 } else { 172 return sql->trans->errnum = PGRES_FATAL_ERROR; 173 } 174 } 175 if (PQsendQuery(sql->conn, query) == 0) { 176 if (TXN_IGNORE_ERRORS(sql->trans)) { 177 PGresult *res = PQexec(sql->conn, 178 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP"); 179 if (res) { 180 int ret = PQresultStatus(res); 181 PQclear(res); 182 if (!dbd_pgsql_is_success(ret)) { 183 sql->trans->errnum = ret; 184 return PGRES_FATAL_ERROR; 185 } 186 } else { 187 return sql->trans->errnum = PGRES_FATAL_ERROR; 188 } 189 } else if (TXN_NOTICE_ERRORS(sql->trans)){ 190 sql->trans->errnum = 1; 191 } 192 return 1; 193 } else { 194 if (TXN_IGNORE_ERRORS(sql->trans)) { 195 PGresult *res = PQexec(sql->conn, 196 "RELEASE SAVEPOINT APR_DBD_TXN_SP"); 197 if (res) { 198 int ret = PQresultStatus(res); 199 PQclear(res); 200 if (!dbd_pgsql_is_success(ret)) { 201 sql->trans->errnum = ret; 202 return PGRES_FATAL_ERROR; 203 } 204 } else { 205 return sql->trans->errnum = PGRES_FATAL_ERROR; 206 } 207 } 208 } 209 if (*results == NULL) { 210 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t)); 211 } 212 (*results)->random = seek; 213 (*results)->handle = sql->conn; 214 (*results)->pool = pool; 215 } 216 return 0; 217} 218 219static const char *dbd_pgsql_get_name(const apr_dbd_results_t *res, int n) 220{ 221 if (res->res) { 222 if ((n>=0) && (PQnfields(res->res) > n)) { 223 return PQfname(res->res,n); 224 } 225 } 226 return NULL; 227} 228 229static int dbd_pgsql_get_row(apr_pool_t *pool, apr_dbd_results_t *res, 230 apr_dbd_row_t **rowp, int rownum) 231{ 232 apr_dbd_row_t *row = *rowp; 233 int sequential = ((rownum >= 0) && res->random) ? 0 : 1; 234 235 if (row == NULL) { 236 row = apr_palloc(pool, sizeof(apr_dbd_row_t)); 237 *rowp = row; 238 row->res = res; 239 if ( sequential ) { 240 row->n = 0; 241 } 242 else { 243 if (rownum > 0) { 244 row->n = --rownum; 245 } 246 else { 247 return -1; /* invalid row */ 248 } 249 } 250 } 251 else { 252 if ( sequential ) { 253 ++row->n; 254 } 255 else { 256 if (rownum > 0) { 257 row->n = --rownum; 258 } 259 else { 260 return -1; /* invalid row */ 261 } 262 } 263 } 264 265 if (res->random) { 266 if ((row->n >= 0) && (size_t)row->n >= res->ntuples) { 267 *rowp = NULL; 268 apr_pool_cleanup_run(pool, res->res, clear_result); 269 res->res = NULL; 270 return -1; 271 } 272 } 273 else { 274 if ((row->n >= 0) && (size_t)row->n >= res->ntuples) { 275 /* no data; we have to fetch some */ 276 row->n -= res->ntuples; 277 if (res->res != NULL) { 278 PQclear(res->res); 279 } 280 res->res = PQgetResult(res->handle); 281 if (res->res) { 282 res->ntuples = PQntuples(res->res); 283 while (res->ntuples == 0) { 284 /* if we got an empty result, clear it, wait a mo, try 285 * again */ 286 PQclear(res->res); 287 apr_sleep(100000); /* 0.1 secs */ 288 res->res = PQgetResult(res->handle); 289 if (res->res) { 290 res->ntuples = PQntuples(res->res); 291 } 292 else { 293 return -1; 294 } 295 } 296 if (res->sz == 0) { 297 res->sz = PQnfields(res->res); 298 } 299 } 300 else { 301 return -1; 302 } 303 } 304 } 305 return 0; 306} 307 308static const char *dbd_pgsql_get_entry(const apr_dbd_row_t *row, int n) 309{ 310 return PQgetvalue(row->res->res, row->n, n); 311} 312 313static apr_status_t dbd_pgsql_datum_get(const apr_dbd_row_t *row, int n, 314 apr_dbd_type_e type, void *data) 315{ 316 if (PQgetisnull(row->res->res, row->n, n)) { 317 return APR_ENOENT; 318 } 319 320 switch (type) { 321 case APR_DBD_TYPE_TINY: 322 *(char*)data = atoi(PQgetvalue(row->res->res, row->n, n)); 323 break; 324 case APR_DBD_TYPE_UTINY: 325 *(unsigned char*)data = atoi(PQgetvalue(row->res->res, row->n, n)); 326 break; 327 case APR_DBD_TYPE_SHORT: 328 *(short*)data = atoi(PQgetvalue(row->res->res, row->n, n)); 329 break; 330 case APR_DBD_TYPE_USHORT: 331 *(unsigned short*)data = atoi(PQgetvalue(row->res->res, row->n, n)); 332 break; 333 case APR_DBD_TYPE_INT: 334 *(int*)data = atoi(PQgetvalue(row->res->res, row->n, n)); 335 break; 336 case APR_DBD_TYPE_UINT: 337 *(unsigned int*)data = atoi(PQgetvalue(row->res->res, row->n, n)); 338 break; 339 case APR_DBD_TYPE_LONG: 340 *(long*)data = atol(PQgetvalue(row->res->res, row->n, n)); 341 break; 342 case APR_DBD_TYPE_ULONG: 343 *(unsigned long*)data = atol(PQgetvalue(row->res->res, row->n, n)); 344 break; 345 case APR_DBD_TYPE_LONGLONG: 346 *(apr_int64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n)); 347 break; 348 case APR_DBD_TYPE_ULONGLONG: 349 *(apr_uint64_t*)data = apr_atoi64(PQgetvalue(row->res->res, row->n, n)); 350 break; 351 case APR_DBD_TYPE_FLOAT: 352 *(float*)data = (float)atof(PQgetvalue(row->res->res, row->n, n)); 353 break; 354 case APR_DBD_TYPE_DOUBLE: 355 *(double*)data = atof(PQgetvalue(row->res->res, row->n, n)); 356 break; 357 case APR_DBD_TYPE_STRING: 358 case APR_DBD_TYPE_TEXT: 359 case APR_DBD_TYPE_TIME: 360 case APR_DBD_TYPE_DATE: 361 case APR_DBD_TYPE_DATETIME: 362 case APR_DBD_TYPE_TIMESTAMP: 363 case APR_DBD_TYPE_ZTIMESTAMP: 364 *(char**)data = PQgetvalue(row->res->res, row->n, n); 365 break; 366 case APR_DBD_TYPE_BLOB: 367 case APR_DBD_TYPE_CLOB: 368 { 369 apr_bucket *e; 370 apr_bucket_brigade *b = (apr_bucket_brigade*)data; 371 372 e = apr_bucket_pool_create(PQgetvalue(row->res->res, row->n, n), 373 PQgetlength(row->res->res, row->n, n), 374 row->res->pool, b->bucket_alloc); 375 APR_BRIGADE_INSERT_TAIL(b, e); 376 } 377 break; 378 case APR_DBD_TYPE_NULL: 379 *(void**)data = NULL; 380 break; 381 default: 382 return APR_EGENERAL; 383 } 384 385 return APR_SUCCESS; 386} 387 388static const char *dbd_pgsql_error(apr_dbd_t *sql, int n) 389{ 390 return PQerrorMessage(sql->conn); 391} 392 393static int dbd_pgsql_query(apr_dbd_t *sql, int *nrows, const char *query) 394{ 395 PGresult *res; 396 int ret; 397 if (sql->trans && sql->trans->errnum) { 398 return sql->trans->errnum; 399 } 400 401 if (TXN_IGNORE_ERRORS(sql->trans)) { 402 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP"); 403 if (res) { 404 int ret = PQresultStatus(res); 405 PQclear(res); 406 if (!dbd_pgsql_is_success(ret)) { 407 sql->trans->errnum = ret; 408 return PGRES_FATAL_ERROR; 409 } 410 } else { 411 return sql->trans->errnum = PGRES_FATAL_ERROR; 412 } 413 } 414 415 res = PQexec(sql->conn, query); 416 if (res) { 417 ret = PQresultStatus(res); 418 if (dbd_pgsql_is_success(ret)) { 419 /* ugh, making 0 return-success doesn't fit */ 420 ret = 0; 421 } 422 *nrows = atoi(PQcmdTuples(res)); 423 PQclear(res); 424 } 425 else { 426 ret = PGRES_FATAL_ERROR; 427 } 428 429 if (ret != 0){ 430 if (TXN_IGNORE_ERRORS(sql->trans)) { 431 PGresult *res = PQexec(sql->conn, 432 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP"); 433 if (res) { 434 int ret = PQresultStatus(res); 435 PQclear(res); 436 if (!dbd_pgsql_is_success(ret)) { 437 sql->trans->errnum = ret; 438 return PGRES_FATAL_ERROR; 439 } 440 } else { 441 sql->trans->errnum = ret; 442 return PGRES_FATAL_ERROR; 443 } 444 } else if (TXN_NOTICE_ERRORS(sql->trans)){ 445 sql->trans->errnum = ret; 446 } 447 } else { 448 if (TXN_IGNORE_ERRORS(sql->trans)) { 449 PGresult *res = PQexec(sql->conn, 450 "RELEASE SAVEPOINT APR_DBD_TXN_SP"); 451 if (res) { 452 int ret = PQresultStatus(res); 453 PQclear(res); 454 if (!dbd_pgsql_is_success(ret)) { 455 sql->trans->errnum = ret; 456 return PGRES_FATAL_ERROR; 457 } 458 } else { 459 sql->trans->errnum = ret; 460 return PGRES_FATAL_ERROR; 461 } 462 } 463 } 464 465 return ret; 466} 467 468static const char *dbd_pgsql_escape(apr_pool_t *pool, const char *arg, 469 apr_dbd_t *sql) 470{ 471 size_t len = strlen(arg); 472 char *ret = apr_palloc(pool, 2*len + 2); 473 PQescapeString(ret, arg, len); 474 return ret; 475} 476 477static int dbd_pgsql_prepare(apr_pool_t *pool, apr_dbd_t *sql, 478 const char *query, const char *label, 479 int nargs, int nvals, apr_dbd_type_e *types, 480 apr_dbd_prepared_t **statement) 481{ 482 char *sqlcmd; 483 char *sqlptr; 484 size_t length, qlen; 485 int i = 0; 486 const char **args; 487 size_t alen; 488 int ret; 489 PGresult *res; 490 491 if (!*statement) { 492 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t)); 493 } 494 (*statement)->nargs = nargs; 495 (*statement)->nvals = nvals; 496 (*statement)->types = types; 497 498 args = apr_palloc(pool, nargs * sizeof(*args)); 499 500 qlen = strlen(query); 501 length = qlen + 1; 502 503 for (i = 0; i < nargs; i++) { 504 switch (types[i]) { 505 case APR_DBD_TYPE_TINY: 506 case APR_DBD_TYPE_UTINY: 507 case APR_DBD_TYPE_SHORT: 508 case APR_DBD_TYPE_USHORT: 509 args[i] = "smallint"; 510 break; 511 case APR_DBD_TYPE_INT: 512 case APR_DBD_TYPE_UINT: 513 args[i] = "integer"; 514 break; 515 case APR_DBD_TYPE_LONG: 516 case APR_DBD_TYPE_ULONG: 517 case APR_DBD_TYPE_LONGLONG: 518 case APR_DBD_TYPE_ULONGLONG: 519 args[i] = "bigint"; 520 break; 521 case APR_DBD_TYPE_FLOAT: 522 args[i] = "real"; 523 break; 524 case APR_DBD_TYPE_DOUBLE: 525 args[i] = "double precision"; 526 break; 527 case APR_DBD_TYPE_TEXT: 528 args[i] = "text"; 529 break; 530 case APR_DBD_TYPE_TIME: 531 args[i] = "time"; 532 break; 533 case APR_DBD_TYPE_DATE: 534 args[i] = "date"; 535 break; 536 case APR_DBD_TYPE_DATETIME: 537 case APR_DBD_TYPE_TIMESTAMP: 538 args[i] = "timestamp"; 539 break; 540 case APR_DBD_TYPE_ZTIMESTAMP: 541 args[i] = "timestamp with time zone"; 542 break; 543 case APR_DBD_TYPE_BLOB: 544 case APR_DBD_TYPE_CLOB: 545 args[i] = "bytea"; 546 break; 547 case APR_DBD_TYPE_NULL: 548 args[i] = "varchar"; /* XXX Eh? */ 549 break; 550 default: 551 args[i] = "varchar"; 552 break; 553 } 554 length += 1 + strlen(args[i]); 555 } 556 557 if (!label) { 558 /* don't really prepare; use in execParams instead */ 559 (*statement)->prepared = 0; 560 (*statement)->name = apr_pstrdup(pool, query); 561 return 0; 562 } 563 (*statement)->name = apr_pstrdup(pool, label); 564 565 /* length of SQL query that prepares this statement */ 566 length = 8 + strlen(label) + 2 + 4 + length + 1; 567 sqlcmd = apr_palloc(pool, length); 568 sqlptr = sqlcmd; 569 memcpy(sqlptr, "PREPARE ", 8); 570 sqlptr += 8; 571 length = strlen(label); 572 memcpy(sqlptr, label, length); 573 sqlptr += length; 574 if (nargs > 0) { 575 memcpy(sqlptr, " (",2); 576 sqlptr += 2; 577 for (i=0; i < nargs; ++i) { 578 alen = strlen(args[i]); 579 memcpy(sqlptr, args[i], alen); 580 sqlptr += alen; 581 *sqlptr++ = ','; 582 } 583 sqlptr[-1] = ')'; 584 } 585 memcpy(sqlptr, " AS ", 4); 586 sqlptr += 4; 587 memcpy(sqlptr, query, qlen); 588 sqlptr += qlen; 589 *sqlptr = 0; 590 591 res = PQexec(sql->conn, sqlcmd); 592 if ( res ) { 593 ret = PQresultStatus(res); 594 if (dbd_pgsql_is_success(ret)) { 595 ret = 0; 596 } 597 /* Hmmm, do we do this here or register it on the pool? */ 598 PQclear(res); 599 } 600 else { 601 ret = PGRES_FATAL_ERROR; 602 } 603 (*statement)->prepared = 1; 604 605 return ret; 606} 607 608static int dbd_pgsql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql, 609 int *nrows, apr_dbd_prepared_t *statement, 610 const char **values, 611 const int *len, const int *fmt) 612{ 613 int ret; 614 PGresult *res; 615 616 if (TXN_IGNORE_ERRORS(sql->trans)) { 617 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP"); 618 if (res) { 619 int ret = PQresultStatus(res); 620 PQclear(res); 621 if (!dbd_pgsql_is_success(ret)) { 622 sql->trans->errnum = ret; 623 return PGRES_FATAL_ERROR; 624 } 625 } else { 626 return sql->trans->errnum = PGRES_FATAL_ERROR; 627 } 628 } 629 630 if (statement->prepared) { 631 res = PQexecPrepared(sql->conn, statement->name, statement->nargs, 632 values, len, fmt, 0); 633 } 634 else { 635 res = PQexecParams(sql->conn, statement->name, statement->nargs, 0, 636 values, len, fmt, 0); 637 } 638 if (res) { 639 ret = PQresultStatus(res); 640 if (dbd_pgsql_is_success(ret)) { 641 ret = 0; 642 } 643 *nrows = atoi(PQcmdTuples(res)); 644 PQclear(res); 645 } 646 else { 647 ret = PGRES_FATAL_ERROR; 648 } 649 650 if (ret != 0){ 651 if (TXN_IGNORE_ERRORS(sql->trans)) { 652 PGresult *res = PQexec(sql->conn, 653 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP"); 654 if (res) { 655 int ret = PQresultStatus(res); 656 PQclear(res); 657 if (!dbd_pgsql_is_success(ret)) { 658 sql->trans->errnum = ret; 659 return PGRES_FATAL_ERROR; 660 } 661 } else { 662 sql->trans->errnum = ret; 663 return PGRES_FATAL_ERROR; 664 } 665 } else if (TXN_NOTICE_ERRORS(sql->trans)){ 666 sql->trans->errnum = ret; 667 } 668 } else { 669 if (TXN_IGNORE_ERRORS(sql->trans)) { 670 PGresult *res = PQexec(sql->conn, 671 "RELEASE SAVEPOINT APR_DBD_TXN_SP"); 672 if (res) { 673 int ret = PQresultStatus(res); 674 PQclear(res); 675 if (!dbd_pgsql_is_success(ret)) { 676 sql->trans->errnum = ret; 677 return PGRES_FATAL_ERROR; 678 } 679 } else { 680 sql->trans->errnum = ret; 681 return PGRES_FATAL_ERROR; 682 } 683 } 684 } 685 686 return ret; 687} 688 689static void dbd_pgsql_bind(apr_dbd_prepared_t *statement, 690 const char **values, 691 const char **val, int *len, int *fmt) 692{ 693 int i, j; 694 695 for (i = 0, j = 0; i < statement->nargs; i++, j++) { 696 if (values[j] == NULL) { 697 val[i] = NULL; 698 } 699 else { 700 switch (statement->types[i]) { 701 case APR_DBD_TYPE_BLOB: 702 case APR_DBD_TYPE_CLOB: 703 val[i] = (char *)values[j]; 704 len[i] = atoi(values[++j]); 705 fmt[i] = 1; 706 707 /* skip table and column */ 708 j += 2; 709 break; 710 default: 711 val[i] = values[j]; 712 break; 713 } 714 } 715 } 716 717 return; 718} 719 720static int dbd_pgsql_pquery(apr_pool_t *pool, apr_dbd_t *sql, 721 int *nrows, apr_dbd_prepared_t *statement, 722 const char **values) 723{ 724 int *len, *fmt; 725 const char **val; 726 727 if (sql->trans && sql->trans->errnum) { 728 return sql->trans->errnum; 729 } 730 731 val = apr_palloc(pool, sizeof(*val) * statement->nargs); 732 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs); 733 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs); 734 735 dbd_pgsql_bind(statement, values, val, len, fmt); 736 737 return dbd_pgsql_pquery_internal(pool, sql, nrows, statement, 738 val, len, fmt); 739} 740 741static int dbd_pgsql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, 742 int *nrows, apr_dbd_prepared_t *statement, 743 va_list args) 744{ 745 const char **values; 746 int i; 747 748 if (sql->trans && sql->trans->errnum) { 749 return sql->trans->errnum; 750 } 751 752 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 753 754 for (i = 0; i < statement->nvals; i++) { 755 values[i] = va_arg(args, const char*); 756 } 757 758 return dbd_pgsql_pquery(pool, sql, nrows, statement, values); 759} 760 761static int dbd_pgsql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql, 762 apr_dbd_results_t **results, 763 apr_dbd_prepared_t *statement, 764 int seek, const char **values, 765 const int *len, const int *fmt) 766{ 767 PGresult *res; 768 int rv; 769 int ret = 0; 770 771 if (seek) { /* synchronous query */ 772 if (TXN_IGNORE_ERRORS(sql->trans)) { 773 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP"); 774 if (res) { 775 int ret = PQresultStatus(res); 776 PQclear(res); 777 if (!dbd_pgsql_is_success(ret)) { 778 sql->trans->errnum = ret; 779 return PGRES_FATAL_ERROR; 780 } 781 } else { 782 sql->trans->errnum = ret; 783 return PGRES_FATAL_ERROR; 784 } 785 } 786 if (statement->prepared) { 787 res = PQexecPrepared(sql->conn, statement->name, statement->nargs, 788 values, len, fmt, 0); 789 } 790 else { 791 res = PQexecParams(sql->conn, statement->name, statement->nargs, 0, 792 values, len, fmt, 0); 793 } 794 if (res) { 795 ret = PQresultStatus(res); 796 if (dbd_pgsql_is_success(ret)) { 797 ret = 0; 798 } 799 else { 800 PQclear(res); 801 } 802 } 803 else { 804 ret = PGRES_FATAL_ERROR; 805 } 806 if (ret != 0) { 807 if (TXN_IGNORE_ERRORS(sql->trans)) { 808 PGresult *res = PQexec(sql->conn, 809 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP"); 810 if (res) { 811 int ret = PQresultStatus(res); 812 PQclear(res); 813 if (!dbd_pgsql_is_success(ret)) { 814 sql->trans->errnum = ret; 815 return PGRES_FATAL_ERROR; 816 } 817 } else { 818 sql->trans->errnum = ret; 819 return PGRES_FATAL_ERROR; 820 } 821 } else if (TXN_NOTICE_ERRORS(sql->trans)){ 822 sql->trans->errnum = ret; 823 } 824 return ret; 825 } else { 826 if (TXN_IGNORE_ERRORS(sql->trans)) { 827 PGresult *res = PQexec(sql->conn, 828 "RELEASE SAVEPOINT APR_DBD_TXN_SP"); 829 if (res) { 830 int ret = PQresultStatus(res); 831 PQclear(res); 832 if (!dbd_pgsql_is_success(ret)) { 833 sql->trans->errnum = ret; 834 return PGRES_FATAL_ERROR; 835 } 836 } else { 837 sql->trans->errnum = ret; 838 return PGRES_FATAL_ERROR; 839 } 840 } 841 } 842 if (!*results) { 843 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t)); 844 } 845 (*results)->res = res; 846 (*results)->ntuples = PQntuples(res); 847 (*results)->sz = PQnfields(res); 848 (*results)->random = seek; 849 (*results)->pool = pool; 850 apr_pool_cleanup_register(pool, res, clear_result, 851 apr_pool_cleanup_null); 852 } 853 else { 854 if (TXN_IGNORE_ERRORS(sql->trans)) { 855 PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP"); 856 if (res) { 857 int ret = PQresultStatus(res); 858 PQclear(res); 859 if (!dbd_pgsql_is_success(ret)) { 860 sql->trans->errnum = ret; 861 return PGRES_FATAL_ERROR; 862 } 863 } else { 864 sql->trans->errnum = ret; 865 return PGRES_FATAL_ERROR; 866 } 867 } 868 if (statement->prepared) { 869 rv = PQsendQueryPrepared(sql->conn, statement->name, 870 statement->nargs, values, len, fmt, 0); 871 } 872 else { 873 rv = PQsendQueryParams(sql->conn, statement->name, 874 statement->nargs, 0, values, len, fmt, 0); 875 } 876 if (rv == 0) { 877 if (TXN_IGNORE_ERRORS(sql->trans)) { 878 PGresult *res = PQexec(sql->conn, 879 "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP"); 880 if (res) { 881 int ret = PQresultStatus(res); 882 PQclear(res); 883 if (!dbd_pgsql_is_success(ret)) { 884 sql->trans->errnum = ret; 885 return PGRES_FATAL_ERROR; 886 } 887 } else { 888 sql->trans->errnum = ret; 889 return PGRES_FATAL_ERROR; 890 } 891 } else if (TXN_NOTICE_ERRORS(sql->trans)){ 892 sql->trans->errnum = 1; 893 } 894 return 1; 895 } else { 896 if (TXN_IGNORE_ERRORS(sql->trans)) { 897 PGresult *res = PQexec(sql->conn, 898 "RELEASE SAVEPOINT APR_DBD_TXN_SP"); 899 if (res) { 900 int ret = PQresultStatus(res); 901 PQclear(res); 902 if (!dbd_pgsql_is_success(ret)) { 903 sql->trans->errnum = ret; 904 return PGRES_FATAL_ERROR; 905 } 906 } else { 907 sql->trans->errnum = ret; 908 return PGRES_FATAL_ERROR; 909 } 910 } 911 } 912 if (!*results) { 913 *results = apr_pcalloc(pool, sizeof(apr_dbd_results_t)); 914 } 915 (*results)->random = seek; 916 (*results)->handle = sql->conn; 917 (*results)->pool = pool; 918 } 919 920 return ret; 921} 922 923static int dbd_pgsql_pselect(apr_pool_t *pool, apr_dbd_t *sql, 924 apr_dbd_results_t **results, 925 apr_dbd_prepared_t *statement, 926 int seek, const char **values) 927{ 928 int *len, *fmt; 929 const char **val; 930 931 if (sql->trans && sql->trans->errnum) { 932 return sql->trans->errnum; 933 } 934 935 val = apr_palloc(pool, sizeof(*val) * statement->nargs); 936 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs); 937 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs); 938 939 dbd_pgsql_bind(statement, values, val, len, fmt); 940 941 return dbd_pgsql_pselect_internal(pool, sql, results, statement, 942 seek, val, len, fmt); 943} 944 945static int dbd_pgsql_pvselect(apr_pool_t *pool, apr_dbd_t *sql, 946 apr_dbd_results_t **results, 947 apr_dbd_prepared_t *statement, 948 int seek, va_list args) 949{ 950 const char **values; 951 int i; 952 953 if (sql->trans && sql->trans->errnum) { 954 return sql->trans->errnum; 955 } 956 957 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 958 959 for (i = 0; i < statement->nvals; i++) { 960 values[i] = va_arg(args, const char*); 961 } 962 963 return dbd_pgsql_pselect(pool, sql, results, statement, seek, values); 964} 965 966static void dbd_pgsql_bbind(apr_pool_t *pool, apr_dbd_prepared_t * statement, 967 const void **values, 968 const char **val, int *len, int *fmt) 969{ 970 int i, j; 971 apr_dbd_type_e type; 972 973 for (i = 0, j = 0; i < statement->nargs; i++, j++) { 974 type = (values[j] == NULL ? APR_DBD_TYPE_NULL : statement->types[i]); 975 976 switch (type) { 977 case APR_DBD_TYPE_TINY: 978 val[i] = apr_itoa(pool, *(char*)values[j]); 979 break; 980 case APR_DBD_TYPE_UTINY: 981 val[i] = apr_itoa(pool, *(unsigned char*)values[j]); 982 break; 983 case APR_DBD_TYPE_SHORT: 984 val[i] = apr_itoa(pool, *(short*)values[j]); 985 break; 986 case APR_DBD_TYPE_USHORT: 987 val[i] = apr_itoa(pool, *(unsigned short*)values[j]); 988 break; 989 case APR_DBD_TYPE_INT: 990 val[i] = apr_itoa(pool, *(int*)values[j]); 991 break; 992 case APR_DBD_TYPE_UINT: 993 val[i] = apr_itoa(pool, *(unsigned int*)values[j]); 994 break; 995 case APR_DBD_TYPE_LONG: 996 val[i] = apr_ltoa(pool, *(long*)values[j]); 997 break; 998 case APR_DBD_TYPE_ULONG: 999 val[i] = apr_ltoa(pool, *(unsigned long*)values[j]); 1000 break; 1001 case APR_DBD_TYPE_LONGLONG: 1002 val[i] = apr_psprintf(pool, "%" APR_INT64_T_FMT, 1003 *(apr_int64_t*)values[j]); 1004 break; 1005 case APR_DBD_TYPE_ULONGLONG: 1006 val[i] = apr_psprintf(pool, "%" APR_UINT64_T_FMT, 1007 *(apr_uint64_t*)values[j]); 1008 break; 1009 case APR_DBD_TYPE_FLOAT: 1010 val[i] = apr_psprintf(pool, "%f", *(float*)values[j]); 1011 break; 1012 case APR_DBD_TYPE_DOUBLE: 1013 val[i] = apr_psprintf(pool, "%lf", *(double*)values[j]); 1014 break; 1015 case APR_DBD_TYPE_STRING: 1016 case APR_DBD_TYPE_TEXT: 1017 case APR_DBD_TYPE_TIME: 1018 case APR_DBD_TYPE_DATE: 1019 case APR_DBD_TYPE_DATETIME: 1020 case APR_DBD_TYPE_TIMESTAMP: 1021 case APR_DBD_TYPE_ZTIMESTAMP: 1022 val[i] = values[j]; 1023 break; 1024 case APR_DBD_TYPE_BLOB: 1025 case APR_DBD_TYPE_CLOB: 1026 val[i] = (char*)values[j]; 1027 len[i] = *(apr_size_t*)values[++j]; 1028 fmt[i] = 1; 1029 1030 /* skip table and column */ 1031 j += 2; 1032 break; 1033 case APR_DBD_TYPE_NULL: 1034 default: 1035 val[i] = NULL; 1036 break; 1037 } 1038 } 1039 1040 return; 1041} 1042 1043static int dbd_pgsql_pbquery(apr_pool_t * pool, apr_dbd_t * sql, 1044 int *nrows, apr_dbd_prepared_t * statement, 1045 const void **values) 1046{ 1047 int *len, *fmt; 1048 const char **val; 1049 1050 if (sql->trans && sql->trans->errnum) { 1051 return sql->trans->errnum; 1052 } 1053 1054 val = apr_palloc(pool, sizeof(*val) * statement->nargs); 1055 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs); 1056 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs); 1057 1058 dbd_pgsql_bbind(pool, statement, values, val, len, fmt); 1059 1060 return dbd_pgsql_pquery_internal(pool, sql, nrows, statement, 1061 val, len, fmt); 1062} 1063 1064static int dbd_pgsql_pvbquery(apr_pool_t * pool, apr_dbd_t * sql, 1065 int *nrows, apr_dbd_prepared_t * statement, 1066 va_list args) 1067{ 1068 const void **values; 1069 int i; 1070 1071 if (sql->trans && sql->trans->errnum) { 1072 return sql->trans->errnum; 1073 } 1074 1075 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1076 1077 for (i = 0; i < statement->nvals; i++) { 1078 values[i] = va_arg(args, const void*); 1079 } 1080 1081 return dbd_pgsql_pbquery(pool, sql, nrows, statement, values); 1082} 1083 1084static int dbd_pgsql_pbselect(apr_pool_t * pool, apr_dbd_t * sql, 1085 apr_dbd_results_t ** results, 1086 apr_dbd_prepared_t * statement, 1087 int seek, const void **values) 1088{ 1089 int *len, *fmt; 1090 const char **val; 1091 1092 if (sql->trans && sql->trans->errnum) { 1093 return sql->trans->errnum; 1094 } 1095 1096 val = apr_palloc(pool, sizeof(*val) * statement->nargs); 1097 len = apr_pcalloc(pool, sizeof(*len) * statement->nargs); 1098 fmt = apr_pcalloc(pool, sizeof(*fmt) * statement->nargs); 1099 1100 dbd_pgsql_bbind(pool, statement, values, val, len, fmt); 1101 1102 return dbd_pgsql_pselect_internal(pool, sql, results, statement, 1103 seek, val, len, fmt); 1104} 1105 1106static int dbd_pgsql_pvbselect(apr_pool_t * pool, apr_dbd_t * sql, 1107 apr_dbd_results_t ** results, 1108 apr_dbd_prepared_t * statement, int seek, 1109 va_list args) 1110{ 1111 const void **values; 1112 int i; 1113 1114 if (sql->trans && sql->trans->errnum) { 1115 return sql->trans->errnum; 1116 } 1117 1118 values = apr_palloc(pool, sizeof(*values) * statement->nvals); 1119 1120 for (i = 0; i < statement->nvals; i++) { 1121 values[i] = va_arg(args, const void*); 1122 } 1123 1124 return dbd_pgsql_pbselect(pool, sql, results, statement, seek, values); 1125} 1126 1127static int dbd_pgsql_start_transaction(apr_pool_t *pool, apr_dbd_t *handle, 1128 apr_dbd_transaction_t **trans) 1129{ 1130 int ret = 0; 1131 PGresult *res; 1132 1133 /* XXX handle recursive transactions here */ 1134 1135 res = PQexec(handle->conn, "BEGIN TRANSACTION"); 1136 if (res) { 1137 ret = PQresultStatus(res); 1138 if (dbd_pgsql_is_success(ret)) { 1139 ret = 0; 1140 if (!*trans) { 1141 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t)); 1142 } 1143 } 1144 PQclear(res); 1145 (*trans)->handle = handle; 1146 handle->trans = *trans; 1147 } 1148 else { 1149 ret = PGRES_FATAL_ERROR; 1150 } 1151 return ret; 1152} 1153 1154static int dbd_pgsql_end_transaction(apr_dbd_transaction_t *trans) 1155{ 1156 PGresult *res; 1157 int ret = -1; /* no transaction is an error cond */ 1158 if (trans) { 1159 /* rollback on error or explicit rollback request */ 1160 if (trans->errnum || TXN_DO_ROLLBACK(trans)) { 1161 trans->errnum = 0; 1162 res = PQexec(trans->handle->conn, "ROLLBACK"); 1163 } 1164 else { 1165 res = PQexec(trans->handle->conn, "COMMIT"); 1166 } 1167 if (res) { 1168 ret = PQresultStatus(res); 1169 if (dbd_pgsql_is_success(ret)) { 1170 ret = 0; 1171 } 1172 PQclear(res); 1173 } 1174 else { 1175 ret = PGRES_FATAL_ERROR; 1176 } 1177 trans->handle->trans = NULL; 1178 } 1179 return ret; 1180} 1181 1182static int dbd_pgsql_transaction_mode_get(apr_dbd_transaction_t *trans) 1183{ 1184 if (!trans) 1185 return APR_DBD_TRANSACTION_COMMIT; 1186 1187 return trans->mode; 1188} 1189 1190static int dbd_pgsql_transaction_mode_set(apr_dbd_transaction_t *trans, 1191 int mode) 1192{ 1193 if (!trans) 1194 return APR_DBD_TRANSACTION_COMMIT; 1195 1196 return trans->mode = (mode & TXN_MODE_BITS); 1197} 1198 1199static void null_notice_receiver(void *arg, const PGresult *res) 1200{ 1201 /* nothing */ 1202} 1203 1204static void null_notice_processor(void *arg, const char *message) 1205{ 1206 /* nothing */ 1207} 1208 1209static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params, 1210 const char **error) 1211{ 1212 apr_dbd_t *sql; 1213 1214 PGconn *conn = PQconnectdb(params); 1215 1216 /* if there's an error in the connect string or something we get 1217 * back a * bogus connection object, and things like PQreset are 1218 * liable to segfault, so just close it out now. it would be nice 1219 * if we could give an indication of why we failed to connect... */ 1220 if (PQstatus(conn) != CONNECTION_OK) { 1221 if (error) { 1222 *error = apr_pstrdup(pool, PQerrorMessage(conn)); 1223 } 1224 PQfinish(conn); 1225 return NULL; 1226 } 1227 1228 PQsetNoticeReceiver(conn, null_notice_receiver, NULL); 1229 PQsetNoticeProcessor(conn, null_notice_processor, NULL); 1230 1231 sql = apr_pcalloc (pool, sizeof (*sql)); 1232 1233 sql->conn = conn; 1234 1235 return sql; 1236} 1237 1238static apr_status_t dbd_pgsql_close(apr_dbd_t *handle) 1239{ 1240 PQfinish(handle->conn); 1241 return APR_SUCCESS; 1242} 1243 1244static apr_status_t dbd_pgsql_check_conn(apr_pool_t *pool, 1245 apr_dbd_t *handle) 1246{ 1247 if (PQstatus(handle->conn) != CONNECTION_OK) { 1248 PQreset(handle->conn); 1249 if (PQstatus(handle->conn) != CONNECTION_OK) { 1250 return APR_EGENERAL; 1251 } 1252 } 1253 return APR_SUCCESS; 1254} 1255 1256static int dbd_pgsql_select_db(apr_pool_t *pool, apr_dbd_t *handle, 1257 const char *name) 1258{ 1259 return APR_ENOTIMPL; 1260} 1261 1262static void *dbd_pgsql_native(apr_dbd_t *handle) 1263{ 1264 return handle->conn; 1265} 1266 1267static int dbd_pgsql_num_cols(apr_dbd_results_t* res) 1268{ 1269 return res->sz; 1270} 1271 1272static int dbd_pgsql_num_tuples(apr_dbd_results_t* res) 1273{ 1274 if (res->random) { 1275 return res->ntuples; 1276 } 1277 else { 1278 return -1; 1279 } 1280} 1281 1282APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_pgsql_driver = { 1283 "pgsql", 1284 NULL, 1285 dbd_pgsql_native, 1286 dbd_pgsql_open, 1287 dbd_pgsql_check_conn, 1288 dbd_pgsql_close, 1289 dbd_pgsql_select_db, 1290 dbd_pgsql_start_transaction, 1291 dbd_pgsql_end_transaction, 1292 dbd_pgsql_query, 1293 dbd_pgsql_select, 1294 dbd_pgsql_num_cols, 1295 dbd_pgsql_num_tuples, 1296 dbd_pgsql_get_row, 1297 dbd_pgsql_get_entry, 1298 dbd_pgsql_error, 1299 dbd_pgsql_escape, 1300 dbd_pgsql_prepare, 1301 dbd_pgsql_pvquery, 1302 dbd_pgsql_pvselect, 1303 dbd_pgsql_pquery, 1304 dbd_pgsql_pselect, 1305 dbd_pgsql_get_name, 1306 dbd_pgsql_transaction_mode_get, 1307 dbd_pgsql_transaction_mode_set, 1308 "$%d", 1309 dbd_pgsql_pvbquery, 1310 dbd_pgsql_pvbselect, 1311 dbd_pgsql_pbquery, 1312 dbd_pgsql_pbselect, 1313 dbd_pgsql_datum_get 1314}; 1315#endif 1316