]> granicus.if.org Git - php/commitdiff
Take a blind stab at implementing scrollable cursors for pgsql.
authorWez Furlong <wez@php.net>
Thu, 13 Jan 2005 01:59:39 +0000 (01:59 +0000)
committerWez Furlong <wez@php.net>
Thu, 13 Jan 2005 01:59:39 +0000 (01:59 +0000)
We allocate a unique cursor name for each statement, so that we
don't interfere with other open statement handles on the same dbh.

Note, however, that we force a new transaction for each open scrollable cursor
(postgres requires cursors to be used inside a transaction).  This is okay,
except for the case where a scrollable cursor is opened, an update is made and
the cursor is closed; closing the cursor commits the transaction that was begun
when it was opened.

It might well be better to avoid the transaction in PDO and force the user to
be aware of the requirements of cursors and explicitly initiate the transaction
themselves.

This is all untested code; it compiles and looks like it will work, but I
encourage someone with a real postgres setup to actually sit down and try to
use it.

ext/pdo_pgsql/pgsql_driver.c
ext/pdo_pgsql/pgsql_statement.c
ext/pdo_pgsql/php_pdo_pgsql_int.h

index c5071e13faf6ff0d7abcaff3a1e61ec6bf42c474..f0815cbadaba798f11a8003706763f0bdc1a903c 100644 (file)
@@ -124,12 +124,30 @@ static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len,
 {
        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
        pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));
+       int ret = 1;
+       int scrollable;
 
        S->H = H;
        stmt->driver_data = S;
        stmt->methods = &pgsql_stmt_methods;
-       
-       return 1;
+
+       scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
+               PDO_CURSOR_FWDONLY TSRMLS_CC) == PDO_CURSOR_SCROLL;
+
+       if (scrollable) {
+               PGresult *res;
+               int ret = 1;
+
+               spprintf(&S->cursor_name, 0, "pdo_pgsql_cursor_%08x", stmt);
+
+               res = PQexec(H->server, "BEGIN");
+               if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+                       ret = 0;
+               }
+               PQclear(res);
+       }
+
+       return ret;
 }
 
 static long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
index 2220cd986cc4ce3770d9df74270e1a5af73a035a..ce20503f9c5dc5f09de657e2213167d44736c375 100644 (file)
@@ -41,6 +41,20 @@ static int pgsql_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
                PQclear(S->result);
                S->result = NULL;
        }
+
+       if (S->cursor_name) {
+               pdo_pgsql_db_handle *H = S->H;
+               char *q = NULL;
+               spprintf(&q, 0, "CLOSE %s", S->cursor_name);
+               PGresult *res = PQexec(H->server, q);
+               efree(q);
+               if (res) PQclear(res);
+               res = PQexec(H->server, "COMMIT");
+               if (res) PQclear(res);
+               efree(S->cursor_name);
+               S->cursor_name = NULL;
+       }
+       
        if(S->cols) {
                efree(S->cols);
                S->cols = NULL;
@@ -64,7 +78,14 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
                }
        }
 
-       S->result = PQexec(H->server, stmt->active_query_string);
+       if (S->cursor_name) {
+               char *q = NULL;
+               spprintf(&q, 0, "DECLARE %s FOR %s", S->cursor_name, stmt->active_query_string);
+               S->result = PQexec(H->server, q);
+               efree(q);
+       } else {
+               S->result = PQexec(H->server, stmt->active_query_string);
+       }
        status = PQresultStatus(S->result);
 
        if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
@@ -77,7 +98,6 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
                S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column));
        }
 
-
        if (status == PGRES_COMMAND_OK) {
                stmt->row_count = (long)atoi(PQcmdTuples(S->result));
        } else {
@@ -98,11 +118,39 @@ static int pgsql_stmt_fetch(pdo_stmt_t *stmt,
 {
        pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
 
-       if (S->current_row < stmt->row_count) {
-               S->current_row++;
-               return 1;
+       if (S->cursor_name) {
+               char *ori_str = NULL;
+               char *q = NULL;
+               ExecStatusType status;
+
+               switch (ori) {
+                       case PDO_FETCH_ORI_NEXT:        ori_str = "NEXT"; break;
+                       case PDO_FETCH_ORI_PRIOR:       ori_str = "PRIOR"; break;
+                       case PDO_FETCH_ORI_REL:         ori_str = "RELATIVE"; break;
+               }
+               if (!ori_str) {
+                       return 0;
+               }
+               
+               spprintf(&q, 0, "FETCH %s %d FROM %s", ori_str, offset, S->cursor_name);
+               S->result = PQexec(S->H->server, q);
+               status = PQresultStatus(S->result);
+
+               if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+                       pdo_pgsql_error_stmt(stmt, status);
+                       return 0;
+               }
+
+               S->current_row = 1;
+               return 1;       
+               
        } else {
-               return 0;
+               if (S->current_row < stmt->row_count) {
+                       S->current_row++;
+                       return 1;
+               } else {
+                       return 0;
+               }
        }
 }
 
index 27d84277b9799a766347a8d4dfa4b0313d66b514..00a6e0d3b739e3495646778aa3f2eeda6cef5198 100644 (file)
@@ -50,6 +50,7 @@ typedef struct {
        PGresult                *result;
        int                     current_row;
        pdo_pgsql_column        *cols;
+       char *cursor_name;
 } pdo_pgsql_stmt;
 
 typedef struct {