Thread: Updateable cursors patch

Updateable cursors patch

From
"FAST PostgreSQL"
Date:
Attached is a working updateable cursors patch. The core functionality has
been implemented and the patch also contains the regression tests and
documentation.

However there are still a couple of TODOs that needs to be done. The planner
has to be taught to treat a DELETE/UPDATE WHERE CURRENT OF as a TidScan.
Currently it follows the sequential scan route and extracts the current tuple
based on the cursor position from the portal.  Also, an issue that needs to
be looked at is how to treat joins with an updateable cursor. Currently it is
allowed and when it comes to delete or update, an error message is thrown
from the executor.

Rgds,
Arul Shaji



Attachment

Re: Updateable cursors patch

From
"Simon Riggs"
Date:
Cool patch.

On Wed, 2007-04-04 at 18:36 +0000, FAST PostgreSQL wrote:
> The planner has to be taught to treat a DELETE/UPDATE WHERE CURRENT OF
> as a TidScan. Currently it follows the sequential scan route and
> extracts the current tuple based on the cursor position from the
> portal.

So you let the planner take a SeqScan, then override this at the top of
the executor? So if we EXPLAIN this it would say "SeqScan", but doesn't
actually do that? It works, but you're right it should do this the same
way as other plan types.

ISTM you need to add a special case in set_plain_rel_pathlist() in
optimizer/paths/allpaths.c similar to the special case for
relation_excluded_by_constraints(). In the case of an updateable cursor
there is only one path we want it to take, so a shortcut out is
appropriate, so the code would look similar-ish. Then you need to teach
the tidscan node to handle the case for an updateable cursor, i.e. a
call similar to GetCursorSlot() in TidNext() in nodeTidscan.c. That way
the rest of the executor machinery can get the slot for you.

Do we need additional code in any of the clients to handle this new
functionality correctly? ECPG etc?

--
  Simon Riggs
  EnterpriseDB   http://www.enterprisedb.com



Re: Updateable cursors patch

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

It will be applied as soon as one of the PostgreSQL committers reviews
and approves it.

---------------------------------------------------------------------------


FAST PostgreSQL wrote:
> Attached is a working updateable cursors patch. The core functionality has
> been implemented and the patch also contains the regression tests and
> documentation.
>
> However there are still a couple of TODOs that needs to be done. The planner
> has to be taught to treat a DELETE/UPDATE WHERE CURRENT OF as a TidScan.
> Currently it follows the sequential scan route and extracts the current tuple
> based on the cursor position from the portal.  Also, an issue that needs to
> be looked at is how to treat joins with an updateable cursor. Currently it is
> allowed and when it comes to delete or update, an error message is thrown
> from the executor.
>
> Rgds,
> Arul Shaji
>
>

[ Attachment, skipping... ]

>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: explain analyze is your friend

--
  Bruce Momjian  <bruce@momjian.us>          http://momjian.us
  EnterpriseDB                               http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: Updateable cursors patch

From
"FAST PostgreSQL"
Date:
On Mon, 2 Apr 2007 03:15, Simon Riggs wrote:
> Cool patch.
>
> On Wed, 2007-04-04 at 18:36 +0000, FAST PostgreSQL wrote:
> > The planner has to be taught to treat a DELETE/UPDATE WHERE CURRENT OF
> > as a TidScan. Currently it follows the sequential scan route and
> > extracts the current tuple based on the cursor position from the
> > portal.
>
> So you let the planner take a SeqScan, then override this at the top of
> the executor? So if we EXPLAIN this it would say "SeqScan", but doesn't
> actually do that? It works, but you're right it should do this the same
> way as other plan types.
>

Yeah, currently EXPLAIN will give the wrong details. That's not ideal of
course. We will be fixing that soon. Thanks for the code tips.

> ISTM you need to add a special case in set_plain_rel_pathlist() in
> optimizer/paths/allpaths.c similar to the special case for
> relation_excluded_by_constraints(). In the case of an updateable cursor
> there is only one path we want it to take, so a shortcut out is
> appropriate, so the code would look similar-ish. Then you need to teach
> the tidscan node to handle the case for an updateable cursor, i.e. a
> call similar to GetCursorSlot() in TidNext() in nodeTidscan.c. That way
> the rest of the executor machinery can get the slot for you.
>
> Do we need additional code in any of the clients to handle this new
> functionality correctly? ECPG etc?
Psql's tab completion is one that quickly comes to mind. Along with the above
said fix, I can do this. Don't know too much about other clients....

Rgds,
Arul Shaji



Re: Updateable cursors patch

From
"Jaime Casanova"
Date:
On 4/4/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
> Attached is a working updateable cursors patch. The core functionality has
> been implemented and the patch also contains the regression tests and
> documentation.
>

this one doesn't apply cleanly to HEAD because of the changes in
http://archives.postgresql.org/pgsql-committers/2007-04/msg00447.php

--
regards,
Jaime Casanova

"Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs and the universe trying
to produce bigger and better idiots.
So far, the universe is winning."
                                       Richard Cook

Re: Updateable cursors patch

From
"FAST PostgreSQL"
Date:
Right. I will send an updated patch against the CVS head in the next
couple of days.

Jaime Casanova wrote:
> On 4/4/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
>> Attached is a working updateable cursors patch. The core functionality
>> has
>> been implemented and the patch also contains the regression tests and
>> documentation.
>>
>
> this one doesn't apply cleanly to HEAD because of the changes in
> http://archives.postgresql.org/pgsql-committers/2007-04/msg00447.php
>


Re: Updateable cursors patch

From
"FAST PostgreSQL"
Date:
Attached is an updated version of the updateable cursors patch against
the latest cvs head.

Most of the changes in the patch are to make it sync with the changes in
CVS recently, for DECLARE CURSOR and EXPLAIN, as mentioned by Jaime in
his mail below.

Rgds,
Arul Shaji


FAST PostgreSQL wrote:
> Right. I will send an updated patch against the CVS head in the next
> couple of days.
>
> Jaime Casanova wrote:
>> On 4/4/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
>>> Attached is a working updateable cursors patch. The core
>>> functionality has
>>> been implemented and the patch also contains the regression tests and
>>> documentation.
>>>
>>
>> this one doesn't apply cleanly to HEAD because of the changes in
>> http://archives.postgresql.org/pgsql-committers/2007-04/msg00447.php
>>
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: explain analyze is your friend
>
>
*** /home/arul/ospg/pgsql/src/backend/parser/gram.y    2007-05-03 18:59:44.000000000 +1000
--- pgbase1705/src/backend/parser/gram.y    2007-05-17 14:41:13.000000000 +1000
***************
*** 297,303 ****
  %type <node>    columnDef
  %type <defelt>    def_elem old_aggr_elem
  %type <node>    def_arg columnElem where_clause
!                 a_expr b_expr c_expr func_expr AexprConst indirection_el
                  columnref in_expr having_clause func_table array_expr
  %type <list>    row type_list array_expr_list
  %type <node>    case_expr case_arg when_clause case_default
--- 297,303 ----
  %type <node>    columnDef
  %type <defelt>    def_elem old_aggr_elem
  %type <node>    def_arg columnElem where_clause
!                 a_expr b_expr c_expr func_expr AexprConst indirection_el where_current_clause
                  columnref in_expr having_clause func_table array_expr
  %type <list>    row type_list array_expr_list
  %type <node>    case_expr case_arg when_clause case_default
***************
*** 377,383 ****
      CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
      COMMITTED CONCURRENTLY CONNECTION CONSTRAINT CONSTRAINTS
      CONTENT_P CONVERSION_P CONVERT COPY COST CREATE CREATEDB
!     CREATEROLE CREATEUSER CROSS CSV CURRENT_DATE CURRENT_ROLE CURRENT_TIME
      CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE

      DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
--- 377,383 ----
      CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
      COMMITTED CONCURRENTLY CONNECTION CONSTRAINT CONSTRAINTS
      CONTENT_P CONVERSION_P CONVERT COPY COST CREATE CREATEDB
!     CREATEROLE CREATEUSER CROSS CSV CURRENT CURRENT_DATE CURRENT_ROLE CURRENT_TIME
      CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE

      DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
***************
*** 5715,5721 ****
   *****************************************************************************/

  DeleteStmt: DELETE_P FROM relation_expr_opt_alias
!             using_clause where_clause returning_clause
                  {
                      DeleteStmt *n = makeNode(DeleteStmt);
                      n->relation = $3;
--- 5715,5721 ----
   *****************************************************************************/

  DeleteStmt: DELETE_P FROM relation_expr_opt_alias
!             using_clause where_current_clause returning_clause
                  {
                      DeleteStmt *n = makeNode(DeleteStmt);
                      n->relation = $3;
***************
*** 5771,5777 ****
  UpdateStmt: UPDATE relation_expr_opt_alias
              SET set_clause_list
              from_clause
!             where_clause
              returning_clause
                  {
                      UpdateStmt *n = makeNode(UpdateStmt);
--- 5771,5777 ----
  UpdateStmt: UPDATE relation_expr_opt_alias
              SET set_clause_list
              from_clause
!             where_current_clause
              returning_clause
                  {
                      UpdateStmt *n = makeNode(UpdateStmt);
***************
*** 5859,5864 ****
--- 5859,5867 ----
                      /* currently we always set FAST_PLAN option */
                      n->options = $3 | $5 | CURSOR_OPT_FAST_PLAN;
                      n->query = $7;
+                     if($7 && ((SelectStmt *)$7)->lockingClause)
+                         if (((LockingClause *)((SelectStmt *)$7)->lockingClause)->forUpdate)
+                             n->options |= CURSOR_OPT_FORUPDATE;
                      $$ = (Node *)n;
                  }
          ;
***************
*** 6563,6568 ****
--- 6566,6581 ----
          ;


+ where_current_clause:
+             WHERE a_expr                            { $$ = $2; }
+             | WHERE CURRENT OF name
+                 {
+                     $$ = (Node *) makeString($4);
+                 }
+             | /*EMPTY*/                                { $$ = NULL; }
+         ;
+
+
  TableFuncElementList:
              TableFuncElement
                  {
***************
*** 9120,9125 ****
--- 9133,9139 ----
              | COLUMN
              | CONSTRAINT
              | CREATE
+             | CURRENT
              | CURRENT_DATE
              | CURRENT_ROLE
              | CURRENT_TIME
*** /home/arul/ospg/pgsql/src/backend/parser/keywords.c    2007-05-03 18:59:44.000000000 +1000
--- pgbase1705/src/backend/parser/keywords.c    2007-05-17 14:44:05.000000000 +1000
***************
*** 101,106 ****
--- 101,107 ----
      {"createuser", CREATEUSER},
      {"cross", CROSS},
      {"csv", CSV},
+     {"current", CURRENT},
      {"current_date", CURRENT_DATE},
      {"current_role", CURRENT_ROLE},
      {"current_time", CURRENT_TIME},
*** /home/arul/ospg/pgsql/src/backend/parser/analyze.c    2007-05-03 18:59:42.000000000 +1000
--- pgbase1705/src/backend/parser/analyze.c    2007-05-17 16:39:20.000000000 +1000
***************
*** 126,131 ****
--- 126,132 ----
                      List **extras_before, List **extras_after);
  static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
                          List **extras_before, List **extras_after);
+ static Node *transformWhereCurrentOfClause();
  static void transformColumnDefinition(ParseState *pstate,
                            CreateStmtContext *cxt,
                            ColumnDef *column);
***************
*** 305,310 ****
--- 306,343 ----
  }

  /*
+  * transformWhereCurrentOfClause
+  *
+  * Eventhough the user gives 'Where current of cursor' it essentially
+  * is 'where ctid = const'. So we create such a where clause and return
+  * it. This will then be transformed like any other where clause. In our
+  * case this will help force a tidscan.
+  *
+  * To make this work with prepared statements, We only set a dummy ctid
+  * now. Later in the executor, this will be replaced by the correct value
+  * from the appropriate portal.
+  */
+ static Node *
+ transformWhereCurrentOfClause()
+ {
+     ColumnRef  *cref;
+     A_Const *cons;
+     Node *whereclause;
+
+     cref = makeNode(ColumnRef);
+     cref->location = -1;
+     cref->fields = lcons(makeString("ctid"), NIL);
+
+     cons = makeNode(A_Const);
+     cons->val.type = T_String;
+     cons->val.val.str = "(0,0)";
+
+     whereclause = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", (Node *)cref, (Node *)cons, -1);
+
+     return whereclause;
+ }
+
+ /*
   * transformStmt -
   *      transform a Parse tree into a Query tree.
   */
***************
*** 410,416 ****
  transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
  {
      Query       *qry = makeNode(Query);
!     Node       *qual;

      qry->commandType = CMD_DELETE;

--- 443,449 ----
  transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
  {
      Query       *qry = makeNode(Query);
!     Node       *qual = NULL;

      qry->commandType = CMD_DELETE;

***************
*** 430,436 ****
       */
      transformFromClause(pstate, stmt->usingClause);

!     qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");

      qry->returningList = transformReturningList(pstate, stmt->returningList);

--- 463,479 ----
       */
      transformFromClause(pstate, stmt->usingClause);

!     if (stmt->whereClause != NULL)
!     {
!         /* WHERE CURRENT OF cursorname clause */
!         if (IsA(stmt->whereClause, String))
!         {
!             qry->cursorName = pstrdup(strVal(stmt->whereClause));
!             stmt->whereClause = transformWhereCurrentOfClause();
!         }
!
!         qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
!     }

      qry->returningList = transformReturningList(pstate, stmt->returningList);

***************
*** 2802,2808 ****
  transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
  {
      Query       *qry = makeNode(Query);
!     Node       *qual;
      ListCell   *origTargetList;
      ListCell   *tl;

--- 2845,2851 ----
  transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
  {
      Query       *qry = makeNode(Query);
!     Node       *qual = NULL;
      ListCell   *origTargetList;
      ListCell   *tl;

***************
*** 2822,2828 ****

      qry->targetList = transformTargetList(pstate, stmt->targetList);

!     qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");

      qry->returningList = transformReturningList(pstate, stmt->returningList);

--- 2865,2880 ----

      qry->targetList = transformTargetList(pstate, stmt->targetList);

!     if (stmt->whereClause != NULL)
!     {
!         /* WHERE CURRENT OF cursorname clause */
!         if (IsA(stmt->whereClause, String))
!         {
!             qry->cursorName = pstrdup(strVal(stmt->whereClause));
!             stmt->whereClause = transformWhereCurrentOfClause();
!         }
!         qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
!     }

      qry->returningList = transformReturningList(pstate, stmt->returningList);

***************
*** 3151,3156 ****
--- 3203,3218 ----
      Query       *result;
      List       *extras_before = NIL,
                 *extras_after = NIL;
+     ListCell        *rm;
+
+     /*
+      * We don't allow an updateable WITH HOLD cursor. We don't support
+      * this feature yet.
+      */
+     if ( (stmt->options & CURSOR_OPT_HOLD) && (stmt->options & CURSOR_OPT_FORUPDATE) )
+         ereport(ERROR,
+                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                 errmsg("Holdable cursors cannot be updated")));

      /*
       * Don't allow both SCROLL and NO SCROLL to be specified
***************
*** 3178,3189 ****
                  (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
                   errmsg("DECLARE CURSOR cannot specify INTO")));

      /* Implementation restriction (might go away someday) */
      if (result->rowMarks != NIL)
!         ereport(ERROR,
!                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!               errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
!                  errdetail("Cursors must be READ ONLY.")));

      /* We won't need the raw querytree any more */
      stmt->query = NULL;
--- 3240,3280 ----
                  (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
                   errmsg("DECLARE CURSOR cannot specify INTO")));

+     /* Disallow implicit/explicit joins, and ORDER BY on FOR UPDATE cursors */
+     if (stmt->options & CURSOR_OPT_FORUPDATE)
+     {
+         if (list_length(result->rtable) != 1)
+             ereport(ERROR,
+                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                 errmsg("Only one target table can be specified for updateable cursors")));
+         else if (result->hasSubLinks)
+             ereport(ERROR,
+                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                 errmsg("Sub-queries cannot be specified for updateable cursors"),
+                 errdetail("SELECT that results in an implicit join is not yet supported")));
+
+         if (list_length(result->sortClause) != 0)
+         {
+             ereport(ERROR,
+                 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+                 errmsg("ORDER BY is not allowed on FOR UPDATE cursors")));
+         }
+     }
+
      /* Implementation restriction (might go away someday) */
      if (result->rowMarks != NIL)
!     {
!         foreach(rm, result->rowMarks)
!         {
!             RowMarkClause    *rmc = (RowMarkClause *) lfirst(rm);
!
!             if (rmc->forUpdate == FALSE)
!                    ereport(ERROR,
!                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                     errmsg("DECLARE CURSOR ... FOR SHARE is not supported"),
!                     errdetail("Cursors must not be FOR SHARE.")));
!         }
!     }

      /* We won't need the raw querytree any more */
      stmt->query = NULL;
*** /home/arul/ospg/pgsql/src/backend/optimizer/path/allpaths.c    2007-05-03 18:59:35.000000000 +1000
--- pgbase1705/src/backend/optimizer/path/allpaths.c    2007-05-17 14:58:52.000000000 +1000
***************
*** 218,223 ****
--- 218,244 ----
          return;
      }

+     /*
+      * For positioned update or delete, we need to force a tid scan. This
+      * is because we already know which row to delete and also the ctid of
+      * the row to be deleted.
+      */
+     if ((root->parse->commandType == CMD_DELETE ||
+         root->parse->commandType == CMD_UPDATE) &&
+         root->parse->cursorName != NULL)
+     {
+         /* Set output-rows estimate to 1 */
+         rel->rows = 1;
+
+         /* Create a tid scan path */
+         create_tidscan_paths(root, rel);
+
+         /* Select cheapest path (pretty easy in this case...) */
+         set_cheapest(rel);
+
+         return;
+     }
+
      /* Mark rel with estimated output rows, width, etc */
      set_baserel_size_estimates(root, rel);

*** /home/arul/ospg/pgsql/src/backend/optimizer/plan/planner.c    2007-05-17 13:01:44.000000000 +1000
--- pgbase1705/src/backend/optimizer/plan/planner.c    2007-05-17 15:02:14.000000000 +1000
***************
*** 170,175 ****
--- 170,185 ----
      result->rowMarks = parse->rowMarks;
      result->nParamExec = list_length(glob->paramlist);

+     /*
+      * If the command is an update or delete on cursor, we need
+      * the cursor name.
+      */
+     if ((parse->commandType == CMD_DELETE ||
+          parse->commandType == CMD_UPDATE) &&
+          parse->cursorName != NULL)
+         /* pass the portal name into the PlannedStmt structure */
+         result->cursorName = pstrdup(parse->cursorName);
+
      return result;
  }

*** /home/arul/ospg/pgsql/src/backend/tcop/pquery.c    2007-05-03 18:59:49.000000000 +1000
--- pgbase1705/src/backend/tcop/pquery.c    2007-05-17 15:04:08.000000000 +1000
***************
*** 940,945 ****
--- 940,957 ----
          else
          {
              ActiveSnapshot = queryDesc->snapshot;
+
+             /*
+              * If this is a SELECT for updateable cursor, store the
+              * resulting ctid.
+              */
+             if (portal->cursorOptions & CURSOR_OPT_FORUPDATE)
+             {
+                 if (ItemPointerIsValid(&queryDesc->estate->es_uc_ctid))
+                     ItemPointerSetInvalid(&queryDesc->estate->es_uc_ctid);
+                 queryDesc->estate->es_store_uctuple = true;
+             }
+
              ExecutorRun(queryDesc, direction, count);
              nprocessed = queryDesc->estate->es_processed;
          }
***************
*** 982,987 ****
--- 994,1011 ----
          else
          {
              ActiveSnapshot = queryDesc->snapshot;
+
+             /*
+              * If this is a SELECT for updateable cursor, store the
+              * resulting ctid.
+              */
+             if (portal->cursorOptions & CURSOR_OPT_FORUPDATE)
+             {
+                 queryDesc->estate->es_store_uctuple = true;
+                 if (ItemPointerIsValid(&queryDesc->estate->es_uc_ctid))
+                     ItemPointerSetInvalid(&queryDesc->estate->es_uc_ctid);
+             }
+
              ExecutorRun(queryDesc, direction, count);
              nprocessed = queryDesc->estate->es_processed;
          }
*** /home/arul/ospg/pgsql/src/backend/executor/execMain.c    2007-05-03 18:59:32.000000000 +1000
--- pgbase1705/src/backend/executor/execMain.c    2007-05-17 15:26:42.000000000 +1000
***************
*** 41,46 ****
--- 41,47 ----
  #include "catalog/toasting.h"
  #include "commands/tablespace.h"
  #include "commands/trigger.h"
+ #include "commands/portalcmds.h"
  #include "executor/execdebug.h"
  #include "executor/instrument.h"
  #include "executor/nodeSubplan.h"
***************
*** 167,172 ****
--- 168,182 ----
      estate->es_crosscheck_snapshot = queryDesc->crosscheck_snapshot;
      estate->es_instrument = queryDesc->doInstrument;

+     /* Determine if this is a positioned delete/update */
+     if ( ((queryDesc->operation == CMD_DELETE) ||
+         (queryDesc->operation == CMD_UPDATE)) &&
+         queryDesc->plannedstmt->cursorName )
+     {
+         estate->es_use_ucplan = true;
+         estate->cursorName = pstrdup(queryDesc->plannedstmt->cursorName);
+     }
+
      /*
       * Initialize the plan state tree
       */
***************
*** 627,632 ****
--- 637,652 ----
      }

      /*
+      * If this is a 'delete/update where current of', we need to update
+      * the plan with the correct ctid. We do it here so that, it will work
+      * with the prepared statements as well.
+      */
+     if (estate->es_use_ucplan)
+     {
+         updateCursorPlan(estate, plan);
+     }
+
+     /*
       * Initialize the private state information for all the nodes in the query
       * tree.  This opens files, allocates storage and leaves us ready to start
       * processing tuples.
*** /home/arul/ospg/pgsql/src/backend/executor/execScan.c    2007-02-02 11:07:03.000000000 +1100
--- pgbase1705/src/backend/executor/execScan.c    2007-05-17 15:11:45.000000000 +1000
***************
*** 65,71 ****
       * all the overhead and return the raw scan tuple.
       */
      if (!qual && !projInfo)
!         return (*accessMtd) (node);

      /*
       * Check to see if we're still projecting out tuples from a previous scan
--- 65,84 ----
       * all the overhead and return the raw scan tuple.
       */
      if (!qual && !projInfo)
!     {
!         resultSlot = (*accessMtd) (node);
!
!         /*
!          * Place the current tuple's ctid in estate if we are scanning
!          * for updateable cursors
!          */
!         if (node->ps.state->es_store_uctuple)
!             if(PointerIsValid(resultSlot) &&
!                          PointerIsValid(resultSlot->tts_tuple) )
!                 ItemPointerCopy(&resultSlot->tts_tuple->t_self,
!                         &node->ps.state->es_uc_ctid);
!         return resultSlot;
!     }

      /*
       * Check to see if we're still projecting out tuples from a previous scan
***************
*** 130,135 ****
--- 143,156 ----
           */
          if (!qual || ExecQual(qual, econtext, false))
          {
+             /*
+              * Place the current tuple's ctid in estate if we are scanning
+              * for updateable cursors
+              */
+             if (node->ps.state->es_store_uctuple)
+                 if(PointerIsValid(slot) && PointerIsValid(slot->tts_tuple) )
+                     ItemPointerCopy(&slot->tts_tuple->t_self,
+                             &node->ps.state->es_uc_ctid);
              /*
               * Found a satisfactory scan tuple.
               */
*** /home/arul/ospg/pgsql/src/backend/utils/mmgr/portalmem.c    2007-05-03 18:59:57.000000000 +1000
--- pgbase1705/src/backend/utils/mmgr/portalmem.c    2007-05-17 15:12:32.000000000 +1000
***************
*** 886,892 ****
       * build tupdesc for result tuples. This must match the definition of
       * the pg_cursors view in system_views.sql
       */
!     tupdesc = CreateTemplateTupleDesc(6, false);
      TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
                         TEXTOID, -1, 0);
      TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
--- 886,892 ----
       * build tupdesc for result tuples. This must match the definition of
       * the pg_cursors view in system_views.sql
       */
!     tupdesc = CreateTemplateTupleDesc(7, false);
      TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
                         TEXTOID, -1, 0);
      TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
***************
*** 897,903 ****
                         BOOLOID, -1, 0);
      TupleDescInitEntry(tupdesc, (AttrNumber) 5, "is_scrollable",
                         BOOLOID, -1, 0);
!     TupleDescInitEntry(tupdesc, (AttrNumber) 6, "creation_time",
                         TIMESTAMPTZOID, -1, 0);

      /*
--- 897,905 ----
                         BOOLOID, -1, 0);
      TupleDescInitEntry(tupdesc, (AttrNumber) 5, "is_scrollable",
                         BOOLOID, -1, 0);
!     TupleDescInitEntry(tupdesc, (AttrNumber) 6, "is_updateable",
!                        BOOLOID, -1, 0);
!     TupleDescInitEntry(tupdesc, (AttrNumber) 7, "creation_time",
                         TIMESTAMPTZOID, -1, 0);

      /*
***************
*** 911,918 ****
      {
          Portal        portal = hentry->portal;
          HeapTuple    tuple;
!         Datum        values[6];
!         bool        nulls[6];

          /* report only "visible" entries */
          if (!portal->visible)
--- 913,920 ----
      {
          Portal        portal = hentry->portal;
          HeapTuple    tuple;
!         Datum        values[7];
!         bool        nulls[7];

          /* report only "visible" entries */
          if (!portal->visible)
***************
*** 932,938 ****
          values[2] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_HOLD);
          values[3] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_BINARY);
          values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
!         values[5] = TimestampTzGetDatum(portal->creation_time);

          tuple = heap_form_tuple(tupdesc, values, nulls);

--- 934,941 ----
          values[2] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_HOLD);
          values[3] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_BINARY);
          values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
!         values[5] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_FORUPDATE);
!         values[6] = TimestampTzGetDatum(portal->creation_time);

          tuple = heap_form_tuple(tupdesc, values, nulls);

*** /home/arul/ospg/pgsql/src/backend/commands/portalcmds.c    2007-05-03 18:59:30.000000000 +1000
--- pgbase1705/src/backend/commands/portalcmds.c    2007-05-17 16:07:11.000000000 +1000
***************
*** 430,432 ****
--- 430,546 ----
       */
      MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
  }
+
+ /*
+  * updateCursorPlan
+  * Updates the plan with the correct ctid. ie the ctid that was stored
+  * in the portal as a result of the previous fetch command. This method
+  * is called only during 'UPDATE/DELETE WHERE CURRENT OF'.
+  *
+  *    estate: estate of the current command
+  *    plan: The current scan plan. Must be a tidscan plan.
+  */
+ void
+ updateCursorPlan(EState *estate, Plan *plan)
+ {
+     QueryDesc *queryDesc;
+     Portal portal;
+     Oid target_relid;
+     ListCell   *l;
+     bool found = false;
+
+     List *quals;
+     TidScan *tidscannode;
+     Node *node;
+
+     /*
+      * Disallow empty-string cursor name (conflicts with protocol-level
+      * unnamed portal).
+      */
+     if (!estate->cursorName || estate->cursorName[0] == '\0')
+         ereport(ERROR,
+                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
+                  errmsg("invalid cursor name: must not be empty")));
+
+     /* Has to be a tidscan. If it isn't we cannot update it */
+     if (! IsA(plan, TidScan))
+         ereport(ERROR,
+                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                  errmsg("invalid planner state")));
+
+     /* get the portal from the portal name */
+     portal = GetPortalByName(estate->cursorName);
+
+     if (!PortalIsValid(portal))
+     {
+         ereport(ERROR,
+                 (errcode(ERRCODE_UNDEFINED_CURSOR),
+                  errmsg("cursor \"%s\" does not exist", estate->cursorName)));
+     }
+     else if (!(portal->cursorOptions & CURSOR_OPT_FORUPDATE))
+     {
+         /* Positioned updates only allowed for updateable cursors. */
+         ereport(ERROR,
+                  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                  errmsg("cursor is not updateable")));
+     }
+
+     queryDesc = PortalGetQueryDesc(portal);
+
+     /*
+      * Search the RTEs for a matching table. We search the whole list eventhough
+      * it will only have one entry now. When we support joins later this should
+      * hold.
+      */
+     target_relid = RelationGetRelid(estate->es_result_relation_info->ri_RelationDesc);
+     foreach(l, queryDesc->plannedstmt->rtable)
+     {
+         RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+         if (rte->relid == target_relid)
+         {
+             found = true;
+             break;
+         }
+     }
+     if (!found)
+     {
+         ereport(ERROR,
+             (errcode(ERRCODE_INVALID_CURSOR_NAME),
+              errmsg("cursor \"%s\" was not declared for table \"%s\"",
+              estate->cursorName,
+              RelationGetRelationName(estate->es_result_relation_info->ri_RelationDesc))));
+     }
+
+     if (ItemPointerIsValid(&queryDesc->estate->es_uc_ctid) )
+     {
+         /*
+         * Retrieve the qual list from the scan plan and update it
+         * with the correct tid.
+         */
+         tidscannode = (TidScan *) plan;
+         quals = (List *)tidscannode->tidquals;
+
+         /* There will be only one entry in the qual list */
+         foreach(l, quals)
+         {
+             node = (Node *) lfirst(l);
+             if (IsA(node, OpExpr))
+             {
+                 List *argslist;
+                 ListCell *argcell;
+
+                 argslist = ((OpExpr *) node)->args;
+                 foreach(argcell, argslist)
+                 {
+                     if (IsA (((Node *)lfirst(argcell)), Const))
+                     {
+                         Const *cons;
+                         cons = (Const *)lfirst(argcell);
+                         /* update the ctid const */
+                         cons->constvalue = PointerGetDatum(&queryDesc->estate->es_uc_ctid);
+                     }
+                 }
+             }
+         }
+     }
+ }
*** /home/arul/ospg/pgsql/src/backend/nodes/copyfuncs.c    2007-05-03 18:59:33.000000000 +1000
--- pgbase1705/src/backend/nodes/copyfuncs.c    2007-05-17 15:21:59.000000000 +1000
***************
*** 84,89 ****
--- 84,90 ----
      COPY_NODE_FIELD(returningLists);
      COPY_NODE_FIELD(rowMarks);
      COPY_SCALAR_FIELD(nParamExec);
+     COPY_STRING_FIELD(cursorName);

      return newnode;
  }
*** /home/arul/ospg/pgsql/src/include/nodes/plannodes.h    2007-05-03 19:00:03.000000000 +1000
--- pgbase1705/src/include/nodes/plannodes.h    2007-05-17 15:21:59.000000000 +1000
***************
*** 69,74 ****
--- 69,77 ----
      List       *rowMarks;        /* a list of RowMarkClause's */

      int            nParamExec;        /* number of PARAM_EXEC Params used */
+
+     char        *cursorName;    /* The updateable cursor name for the current
+                      * UPDATE/DELETE stmt */
  } PlannedStmt;

  /* macro for fetching the Plan associated with a SubPlan node */
*** /home/arul/ospg/pgsql/src/include/nodes/parsenodes.h    2007-05-03 19:00:03.000000000 +1000
--- pgbase1705/src/include/nodes/parsenodes.h    2007-05-17 15:21:59.000000000 +1000
***************
*** 131,136 ****
--- 131,139 ----

      Node       *setOperations;    /* set-operation tree if this is top level of
                                   * a UNION/INTERSECT/EXCEPT query */
+
+     char        *cursorName;    /* The updateable cursor name for the current
+                      * UPDATE/DELETE query */
  } Query;


***************
*** 1441,1446 ****
--- 1444,1450 ----
  #define CURSOR_OPT_INSENSITIVE    0x0008        /* INSENSITIVE (unimplemented) */
  #define CURSOR_OPT_HOLD            0x0010        /* WITH HOLD */
  #define CURSOR_OPT_FAST_PLAN    0x0020        /* prefer fast-start plan */
+ #define CURSOR_OPT_FORUPDATE    0x0040        /* Updateable cursor */

  typedef struct DeclareCursorStmt
  {
*** /home/arul/ospg/pgsql/src/include/commands/portalcmds.h    2007-05-03 19:00:01.000000000 +1000
--- pgbase1705/src/include/commands/portalcmds.h    2007-05-17 15:21:59.000000000 +1000
***************
*** 31,34 ****
--- 31,36 ----

  extern void PersistHoldablePortal(Portal portal);

+ extern void updateCursorPlan(EState *estate, Plan *plan);
+
  #endif   /* PORTALCMDS_H */
*** /home/arul/ospg/pgsql/src/include/nodes/execnodes.h    2007-05-17 13:02:10.000000000 +1000
--- pgbase1705/src/include/nodes/execnodes.h    2007-05-17 15:21:59.000000000 +1000
***************
*** 349,354 ****
--- 349,360 ----
      bool       *es_evTupleNull; /* local array of EPQ status */
      HeapTuple  *es_evTuple;        /* shared array of EPQ substitute tuples */
      bool        es_useEvalPlan; /* evaluating EPQ tuples? */
+
+     /* Info for storing updateable cursor details */
+     bool        es_store_uctuple;
+     bool        es_use_ucplan;
+     char        *cursorName;
+     ItemPointerData    es_uc_ctid;    /* ctid for updateable cursors. */
  } EState;


*** /home/arul/ospg/pgsql/src/backend/catalog/system_views.sql    2007-04-12 17:02:11.000000000 +1000
--- pgbase1705/src/backend/catalog/system_views.sql    2007-05-17 15:21:59.000000000 +1000
***************
*** 150,159 ****

  CREATE VIEW pg_cursors AS
      SELECT C.name, C.statement, C.is_holdable, C.is_binary,
!            C.is_scrollable, C.creation_time
      FROM pg_cursor() AS C
           (name text, statement text, is_holdable boolean, is_binary boolean,
!           is_scrollable boolean, creation_time timestamptz);

  CREATE VIEW pg_prepared_xacts AS
      SELECT P.transaction, P.gid, P.prepared,
--- 150,159 ----

  CREATE VIEW pg_cursors AS
      SELECT C.name, C.statement, C.is_holdable, C.is_binary,
!             C.is_scrollable, C.is_updateable, C.creation_time
      FROM pg_cursor() AS C
           (name text, statement text, is_holdable boolean, is_binary boolean,
!            is_scrollable boolean, is_updateable boolean, creation_time timestamptz);

  CREATE VIEW pg_prepared_xacts AS
      SELECT P.transaction, P.gid, P.prepared,
*** /home/arul/ospg/pgsql/src/test/regress/expected/rules.out    2007-04-12 17:02:49.000000000 +1000
--- pgbase1705/src/test/regress/expected/rules.out    2007-05-17 15:21:59.000000000 +1000
***************
*** 1279,1285 ****
           viewname         |




definition



                                                                                                      

--------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   iexit                    | SELECT ih."name", ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih,
rampr WHERE (ih.thepath ## r.thepath); 
!  pg_cursors               | SELECT c."name", c."statement", c.is_holdable, c.is_binary, c.is_scrollable,
c.creation_timeFROM pg_cursor() c("name" text, "statement" text, is_holdable boolean, is_binary boolean, is_scrollable
boolean,creation_time timestamp with time zone); 
   pg_group                 | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT
pg_auth_members.memberFROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid
WHERE(NOT pg_authid.rolcanlogin); 
   pg_indexes               | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname
AS"tablespace", pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN
pg_classi ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace
tON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")); 
   pg_locks                 | SELECT l.locktype, l."database", l.relation, l.page, l.tuple, l.transactionid, l.classid,
l.objid,l.objsubid, l."transaction", l.pid, l."mode", l."granted" FROM pg_lock_status() l(locktype text, "database"
oid,relation oid, page integer, tuple smallint, transactionid xid, classid oid, objid oid, objsubid smallint,
"transaction"xid, pid integer, "mode" text, "granted" boolean); 
--- 1279,1285 ----
           viewname         |




definition



                                                                                                      

--------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   iexit                    | SELECT ih."name", ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih,
rampr WHERE (ih.thepath ## r.thepath); 
!  pg_cursors               | SELECT c."name", c."statement", c.is_holdable, c.is_binary, c.is_scrollable,
c.is_updateable,c.creation_time FROM pg_cursor() c("name" text, "statement" text, is_holdable boolean, is_binary
boolean,is_scrollable boolean, is_updateable boolean, creation_time timestamp with time zone); 
   pg_group                 | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT
pg_auth_members.memberFROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid
WHERE(NOT pg_authid.rolcanlogin); 
   pg_indexes               | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname
AS"tablespace", pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN
pg_classi ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace
tON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")); 
   pg_locks                 | SELECT l.locktype, l."database", l.relation, l.page, l.tuple, l.transactionid, l.classid,
l.objid,l.objsubid, l."transaction", l.pid, l."mode", l."granted" FROM pg_lock_status() l(locktype text, "database"
oid,relation oid, page integer, tuple smallint, transactionid xid, classid oid, objid oid, objsubid smallint,
"transaction"xid, pid integer, "mode" text, "granted" boolean); 
*** /home/arul/ospg/pgsql/src/test/regress/expected/portals.out    2007-05-03 19:00:09.000000000 +1000
--- pgbase1705/src/test/regress/expected/portals.out    2007-05-17 18:10:05.000000000 +1000
***************
*** 869,874 ****
--- 869,1011 ----
   c2   | declare c2 cursor with hold for select count_tt1_v(), count_tt1_s(); | t           | f         | f
  (1 row)

+ -- Create an updatable cursor and show that delete at position works
+ CREATE TABLE t4(i int, j text);
+ INSERT INTO t4 VALUES(1, 'one');
+ INSERT INTO t4 VALUES(2, 'two');
+ INSERT INTO t4 VALUES(3, 'three');
+ INSERT INTO t4 VALUES(4, 'four');
+ INSERT INTO t4 VALUES(5, 'five');
+ INSERT INTO t4 VALUES(6, 'six');
+ INSERT INTO t4 VALUES(7, 'seven');
+ INSERT INTO t4 VALUES(8, 'eight');
+ INSERT INTO t4 VALUES(9, 'nine');
+ SELECT * FROM t4;
+  i |   j
+ ---+-------
+  1 | one
+  2 | two
+  3 | three
+  4 | four
+  5 | five
+  6 | six
+  7 | seven
+  8 | eight
+  9 | nine
+ (9 rows)
+
+ BEGIN;
+ DECLARE c1 CURSOR FOR SELECT * FROM t4 FOR UPDATE;
+ FETCH c1;
+  i |  j
+ ---+-----
+  1 | one
+ (1 row)
+
+ DELETE FROM t4 WHERE CURRENT OF c1;
+ FETCH ALL FROM c1;
+  i |   j
+ ---+-------
+  2 | two
+  3 | three
+  4 | four
+  5 | five
+  6 | six
+  7 | seven
+  8 | eight
+  9 | nine
+ (8 rows)
+
+ SELECT * FROM t4;
+  i |   j
+ ---+-------
+  2 | two
+  3 | three
+  4 | four
+  5 | five
+  6 | six
+  7 | seven
+  8 | eight
+  9 | nine
+ (8 rows)
+
+ CLOSE c1;
+ COMMIT;
+ -- Create an updatable cursor and show that update at position works
+ SELECT * FROM t4;
+  i |   j
+ ---+-------
+  2 | two
+  3 | three
+  4 | four
+  5 | five
+  6 | six
+  7 | seven
+  8 | eight
+  9 | nine
+ (8 rows)
+
+ BEGIN;
+ DECLARE c1 CURSOR FOR SELECT * FROM t4 FOR UPDATE;
+ FETCH c1;
+  i |  j
+ ---+-----
+  2 | two
+ (1 row)
+
+ UPDATE t4 SET i = 8 WHERE CURRENT OF c1;
+ SELECT * FROM t4;
+  i |   j
+ ---+-------
+  3 | three
+  4 | four
+  5 | five
+  6 | six
+  7 | seven
+  8 | eight
+  9 | nine
+  8 | two
+ (8 rows)
+
+ CLOSE c1;
+ COMMIT;
+ -- Updatable cursor combined with MOVE
+ BEGIN;
+ DECLARE c1 CURSOR FOR SELECT * FROM t4 FOR UPDATE;
+ FETCH c1;
+  i |   j
+ ---+-------
+  3 | three
+ (1 row)
+
+ UPDATE t4 SET i = 10 WHERE CURRENT OF c1;
+ MOVE 5 IN c1;
+ FETCH c1;
+  i |  j
+ ---+------
+  9 | nine
+ (1 row)
+
+ UPDATE t4 SET i = 12 WHERE CURRENT OF c1;
+ CLOSE c1;
+ COMMIT;
+ -- Update on j, ordering on i
+ BEGIN;
+ DECLARE c1 CURSOR FOR SELECT j FROM t4 ORDER BY i FOR UPDATE OF i;
+ ERROR:  relation "i" in FOR UPDATE/SHARE clause not found in FROM clause
+ ROLLBACK;
+ -- Attempt a DELETE WHERE CURRENT OF prior to FETCHing a row, should fail
+ BEGIN;
+ DECLARE c1 CURSOR FOR SELECT * FROM t4 FOR UPDATE;
+ -- should fail
+ DELETE FROM t4 WHERE CURRENT OF c1;
+ ROLLBACK;
+ --Attempt to use a WITH HOLD updatable cursor, should fail
+ BEGIN;
+ DECLARE c1 CURSOR WITH HOLD FOR SELECT * FROM t4 FOR UPDATE;
+ ERROR:  Holdable cursors cannot be updated
+ ROLLBACK;
+ DROP TABLE t4;
  -- test CLOSE ALL;
  SELECT name FROM pg_cursors ORDER BY 1;
   name
*** /home/arul/ospg/pgsql/src/test/regress/sql/portals.sql    2007-05-03 19:00:10.000000000 +1000
--- pgbase1705/src/test/regress/sql/portals.sql    2007-05-17 17:58:41.000000000 +1000
***************
*** 304,309 ****
--- 304,370 ----
    SELECT name, statement, is_holdable, is_binary, is_scrollable FROM pg_cursors;
  EXECUTE cprep;

+
+ -- Create an updatable cursor and show that delete at position works
+ CREATE TABLE t4(i int, j text);
+ INSERT INTO t4 VALUES(1, 'one');
+ INSERT INTO t4 VALUES(2, 'two');
+ INSERT INTO t4 VALUES(3, 'three');
+ INSERT INTO t4 VALUES(4, 'four');
+ INSERT INTO t4 VALUES(5, 'five');
+ INSERT INTO t4 VALUES(6, 'six');
+ INSERT INTO t4 VALUES(7, 'seven');
+ INSERT INTO t4 VALUES(8, 'eight');
+ INSERT INTO t4 VALUES(9, 'nine');
+
+ SELECT * FROM t4;
+ BEGIN;
+ DECLARE c1 CURSOR FOR SELECT * FROM t4 FOR UPDATE;
+ FETCH c1;
+ DELETE FROM t4 WHERE CURRENT OF c1;
+ FETCH ALL FROM c1;
+ SELECT * FROM t4;
+ CLOSE c1;
+ COMMIT;
+
+ -- Create an updatable cursor and show that update at position works
+ SELECT * FROM t4;
+ BEGIN;
+ DECLARE c1 CURSOR FOR SELECT * FROM t4 FOR UPDATE;
+ FETCH c1;
+ UPDATE t4 SET i = 8 WHERE CURRENT OF c1;
+ SELECT * FROM t4;
+ CLOSE c1;
+ COMMIT;
+
+ -- Updatable cursor combined with MOVE
+ BEGIN;
+ DECLARE c1 CURSOR FOR SELECT * FROM t4 FOR UPDATE;
+ FETCH c1;
+ UPDATE t4 SET i = 10 WHERE CURRENT OF c1;
+ MOVE 5 IN c1;
+ FETCH c1;
+ UPDATE t4 SET i = 12 WHERE CURRENT OF c1;
+ CLOSE c1;
+ COMMIT;
+ -- Update on j, ordering on i
+ BEGIN;
+ DECLARE c1 CURSOR FOR SELECT j FROM t4 ORDER BY i FOR UPDATE OF i;
+ ROLLBACK;
+
+ -- Attempt a DELETE WHERE CURRENT OF prior to FETCHing a row, should fail
+ BEGIN;
+ DECLARE c1 CURSOR FOR SELECT * FROM t4 FOR UPDATE;
+ -- should fail
+ DELETE FROM t4 WHERE CURRENT OF c1;
+ ROLLBACK;
+
+ --Attempt to use a WITH HOLD updatable cursor, should fail
+ BEGIN;
+ DECLARE c1 CURSOR WITH HOLD FOR SELECT * FROM t4 FOR UPDATE;
+ ROLLBACK;
+ DROP TABLE t4;
+
  -- test CLOSE ALL;
  SELECT name FROM pg_cursors ORDER BY 1;
  CLOSE ALL;
*** /home/arul/ospg/pgsql/doc/src/sgml/ref/delete.sgml    2007-02-01 11:28:19.000000000 +1100
--- pgbase1705/doc/src/sgml/ref/delete.sgml    2007-05-17 17:58:41.000000000 +1000
***************
*** 22,28 ****
  <synopsis>
  DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <replaceable
class="parameter">alias</replaceable>] 
      [ USING <replaceable class="PARAMETER">usinglist</replaceable> ]
!     [ WHERE <replaceable class="PARAMETER">condition</replaceable> ]
      [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ AS <replaceable
class="parameter">output_name</replaceable>] [, ...] ] 
  </synopsis>
   </refsynopsisdiv>
--- 22,28 ----
  <synopsis>
  DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <replaceable
class="parameter">alias</replaceable>] 
      [ USING <replaceable class="PARAMETER">usinglist</replaceable> ]
!     [ [ WHERE <replaceable class="PARAMETER">condition</replaceable> ] | [ WHERE CURRENT OF <replaceable
class="PARAMETER">cursor_name</replaceable>] ] 
      [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ AS <replaceable
class="parameter">output_name</replaceable>] [, ...] ] 
  </synopsis>
   </refsynopsisdiv>
***************
*** 162,167 ****
--- 162,176 ----
       </para>
      </listitem>
     </varlistentry>
+
+    <varlistentry>
+     <term><replaceable class="PARAMETER">cursor_name</replaceable></term>
+     <listitem>
+      <para>
+       The name of the cursor to use with <command>WHERE CURRENT OF</> condition.
+      </para>
+     </listitem>
+    </varlistentry>
    </variablelist>
   </refsect1>

***************
*** 238,243 ****
--- 247,259 ----
  DELETE FROM tasks WHERE status = 'DONE' RETURNING *;
  </programlisting>
    </para>
+
+    <para>
+    Delete the task, on which the cursor c_tasks is pointing currently:
+ <programlisting>
+ DELETE FROM tasks WHERE CURRENT OF c_tasks;
+ </programlisting>
+   </para>
   </refsect1>

   <refsect1>
*** /home/arul/ospg/pgsql/doc/src/sgml/ref/declare.sgml    2007-02-01 10:26:03.000000000 +1100
--- pgbase1705/doc/src/sgml/ref/declare.sgml    2007-05-17 17:58:41.000000000 +1000
***************
*** 171,181 ****
       <para>
        <literal>FOR READ ONLY</literal> indicates that the cursor will
        be used in a read-only mode.  <literal>FOR UPDATE</literal>
!       indicates that the cursor will be used to update tables.  Since
!       cursor updates are not currently supported in
!       <productname>PostgreSQL</productname>, specifying <literal>FOR
!       UPDATE</literal> will cause an error message and specifying
!       <literal>FOR READ ONLY</literal> has no effect.
       </para>
      </listitem>
     </varlistentry>
--- 171,177 ----
       <para>
        <literal>FOR READ ONLY</literal> indicates that the cursor will
        be used in a read-only mode.  <literal>FOR UPDATE</literal>
!       indicates that the cursor will be used to update tables.
       </para>
      </listitem>
     </varlistentry>
***************
*** 184,191 ****
      <term><replaceable class="parameter">column</replaceable></term>
      <listitem>
       <para>
!       Column(s) to be updated by the cursor.  Since cursor updates are
!       not currently supported in
        <productname>PostgreSQL</productname>, the <literal>FOR
        UPDATE</literal> clause provokes an error message.
       </para>
--- 180,187 ----
      <term><replaceable class="parameter">column</replaceable></term>
      <listitem>
       <para>
!       Column(s) to be updated by the cursor.  Since cursor updates on column(s)
!       are not currently supported in
        <productname>PostgreSQL</productname>, the <literal>FOR
        UPDATE</literal> clause provokes an error message.
       </para>
***************
*** 286,296 ****
    </para>

    <para>
-    The SQL standard allows cursors to update table data. All
-    <productname>PostgreSQL</> cursors are read only.
-   </para>
-
-   <para>
     Binary cursors are a <productname>PostgreSQL</productname>
     extension.
    </para>
--- 282,287 ----
*** /home/arul/ospg/pgsql/doc/src/sgml/ref/update.sgml    2007-02-01 11:28:19.000000000 +1100
--- pgbase1705/doc/src/sgml/ref/update.sgml    2007-05-17 17:58:41.000000000 +1000
***************
*** 24,30 ****
      SET { <replaceable class="PARAMETER">column</replaceable> = { <replaceable
class="PARAMETER">expression</replaceable>| DEFAULT } | 
            ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) = ( { <replaceable
class="PARAMETER">expression</replaceable>| DEFAULT } [, ...] ) } [, ...] 
      [ FROM <replaceable class="PARAMETER">fromlist</replaceable> ]
!     [ WHERE <replaceable class="PARAMETER">condition</replaceable> ]
      [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ AS <replaceable
class="parameter">output_name</replaceable>] [, ...] ] 
  </synopsis>
   </refsynopsisdiv>
--- 24,30 ----
      SET { <replaceable class="PARAMETER">column</replaceable> = { <replaceable
class="PARAMETER">expression</replaceable>| DEFAULT } | 
            ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) = ( { <replaceable
class="PARAMETER">expression</replaceable>| DEFAULT } [, ...] ) } [, ...] 
      [ FROM <replaceable class="PARAMETER">fromlist</replaceable> ]
!     [ [ WHERE <replaceable class="PARAMETER">condition</replaceable> ] | [ WHERE CURRENT OF <replaceable
class="PARAMETER">cursor_name</replaceable>] ] 
      [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ AS <replaceable
class="parameter">output_name</replaceable>] [, ...] ] 
  </synopsis>
   </refsynopsisdiv>
***************
*** 181,186 ****
--- 181,195 ----
       </para>
      </listitem>
     </varlistentry>
+
+    <varlistentry>
+     <term><replaceable class="PARAMETER">cursor_name</replaceable></term>
+     <listitem>
+      <para>
+       The name of the cursor to use with <command>WHERE CURRENT OF</> condition.
+      </para>
+     </listitem>
+    </varlistentry>
    </variablelist>
   </refsect1>

***************
*** 244,249 ****
--- 253,269 ----
  </programlisting>
    </para>

+    <para>
+    Change the word to <literal>Dramatic</> in the
+    column <structfield>kind</> of the table <structname>films</structname>,
+    on the row on which the cursor c_tasks is positioned currently:
+
+ <programlisting>
+ UPDATE films SET kind = 'Dramatic' WHERE CURRENT OF c_tasks;
+ </programlisting>
+   </para>
+
+
    <para>
     Adjust temperature entries and reset precipitation to its default
     value in one row of the table <structname>weather</structname>:

Re: Updateable cursors patch

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

It will be applied as soon as one of the PostgreSQL committers reviews
and approves it.

---------------------------------------------------------------------------


FAST PostgreSQL wrote:
> Attached is an updated version of the updateable cursors patch against
> the latest cvs head.
>
> Most of the changes in the patch are to make it sync with the changes in
> CVS recently, for DECLARE CURSOR and EXPLAIN, as mentioned by Jaime in
> his mail below.
>
> Rgds,
> Arul Shaji
>
>
> FAST PostgreSQL wrote:
> > Right. I will send an updated patch against the CVS head in the next
> > couple of days.
> >
> > Jaime Casanova wrote:
> >> On 4/4/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
> >>> Attached is a working updateable cursors patch. The core
> >>> functionality has
> >>> been implemented and the patch also contains the regression tests and
> >>> documentation.
> >>>
> >>
> >> this one doesn't apply cleanly to HEAD because of the changes in
> >> http://archives.postgresql.org/pgsql-committers/2007-04/msg00447.php
> >>
> >
> >
> > ---------------------------(end of broadcast)---------------------------
> > TIP 6: explain analyze is your friend
> >
> >


>
> ---------------------------(end of broadcast)---------------------------
> TIP 7: You can help support the PostgreSQL project by donating at
>
>                 http://www.postgresql.org/about/donate

--
  Bruce Momjian  <bruce@momjian.us>          http://momjian.us
  EnterpriseDB                               http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: Updateable cursors patch

From
"Pavel Stehule"
Date:
Hello

I am not sure, but your solution will faill on scrollable cursors
(it's similar to holdable cursors). I miss part about limits in
documentation. Propably updatable cursors aren't supported by plpgsql
(and it's point to ToDo).

Regards
Pavel Stehule

2007/5/17, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au>:
> Attached is an updated version of the updateable cursors patch against
> the latest cvs head.
>
> Most of the changes in the patch are to make it sync with the changes in
> CVS recently, for DECLARE CURSOR and EXPLAIN, as mentioned by Jaime in
> his mail below.
>
> Rgds,
> Arul Shaji
>
>
> FAST PostgreSQL wrote:
> > Right. I will send an updated patch against the CVS head in the next
> > couple of days.
> >
> > Jaime Casanova wrote:
> >> On 4/4/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
> >>> Attached is a working updateable cursors patch. The core
> >>> functionality has
> >>> been implemented and the patch also contains the regression tests and
> >>> documentation.
> >>>
> >>
> >> this one doesn't apply cleanly to HEAD because of the changes in
> >> http://archives.postgresql.org/pgsql-committers/2007-04/msg00447.php
> >>
> >
> >
> > ---------------------------(end of broadcast)---------------------------
> > TIP 6: explain analyze is your friend
> >
> >
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 7: You can help support the PostgreSQL project by donating at
>
>                 http://www.postgresql.org/about/donate
>
>
>

Re: Updateable cursors patch

From
"FAST PostgreSQL"
Date:
No. It works with scrollable cursors. It will work for cursors/selects
which does not put the results in some store, such as WITH hold/group
by/order by etc.... But most of these restrictions apply for normal
'Select for update' anyway. (With the order by clause, the
implementation is as per the sql standards.)

I can update the documentation once the initial review is done and what
I have done gets atleast a pass mark :-)

Rgds,
Arul Shaji


Pavel Stehule wrote:
> Hello
>
> I am not sure, but your solution will faill on scrollable cursors
> (it's similar to holdable cursors). I miss part about limits in
> documentation. Propably updatable cursors aren't supported by plpgsql
> (and it's point to ToDo).
>
> Regards
> Pavel Stehule
>
> 2007/5/17, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au>:
>> Attached is an updated version of the updateable cursors patch against
>> the latest cvs head.
>>
>> Most of the changes in the patch are to make it sync with the changes in
>> CVS recently, for DECLARE CURSOR and EXPLAIN, as mentioned by Jaime in
>> his mail below.
>>
>> Rgds,
>> Arul Shaji
>>
>>
>> FAST PostgreSQL wrote:
>> > Right. I will send an updated patch against the CVS head in the next
>> > couple of days.
>> >
>> > Jaime Casanova wrote:
>> >> On 4/4/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
>> >>> Attached is a working updateable cursors patch. The core
>> >>> functionality has
>> >>> been implemented and the patch also contains the regression tests and
>> >>> documentation.
>> >>>
>> >>
>> >> this one doesn't apply cleanly to HEAD because of the changes in
>> >> http://archives.postgresql.org/pgsql-committers/2007-04/msg00447.php
>> >>
>> >
>> >
>> > ---------------------------(end of
>> broadcast)---------------------------
>> > TIP 6: explain analyze is your friend
>> >
>> >
>>
>>
>> ---------------------------(end of broadcast)---------------------------
>> TIP 7: You can help support the PostgreSQL project by donating at
>>
>>                 http://www.postgresql.org/about/donate
>>
>>
>>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 3: Have you checked our extensive FAQ?
>
>               http://www.postgresql.org/docs/faq
>
>


Re: Updateable cursors patch

From
"Jaime Casanova"
Date:
On 5/17/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
> No. It works with scrollable cursors. It will work for cursors/selects
> which does not put the results in some store, such as WITH hold/group
> by/order by etc.... But most of these restrictions apply for normal
> 'Select for update' anyway. (With the order by clause, the
> implementation is as per the sql standards.)
>

your patch doesn't work with updatable views because they don't have
ctid columns....

ERROR:  column "ctid" does not exist
STATEMENT:  update vfoo set des_cta = des_cta || ' - prueba' where
current of foo;
ERROR:  current transaction is aborted, commands ignored until end of
transaction block

is this sane behavior? to accept create cursors for update on views
and then failing to update "where current of" and rollback the entire
transaction?

comments?

--
regards,
Jaime Casanova

"Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs and the universe trying
to produce bigger and better idiots.
So far, the universe is winning."
                                       Richard Cook

Re: Updateable cursors patch

From
"Jaime Casanova"
Date:
On 5/17/07, Jaime Casanova <systemguards@gmail.com> wrote:
> On 5/17/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
> > No. It works with scrollable cursors. It will work for cursors/selects
> > which does not put the results in some store, such as WITH hold/group
> > by/order by etc.... But most of these restrictions apply for normal
> > 'Select for update' anyway. (With the order by clause, the
> > implementation is as per the sql standards.)
> >
>
> your patch doesn't work with updatable views because they don't have
> ctid columns....
>
> ERROR:  column "ctid" does not exist
> STATEMENT:  update vfoo set des_cta = des_cta || ' - prueba' where
> current of foo;
> ERROR:  current transaction is aborted, commands ignored until end of
> transaction block
>
> is this sane behavior? to accept create cursors for update on views
> and then failing to update "where current of" and rollback the entire
> transaction?
>
> comments?
>

sorry, reattaching the test script

--
regards,
Jaime Casanova

"Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs and the universe trying
to produce bigger and better idiots.
So far, the universe is winning."
                                       Richard Cook

Attachment

Re: Updateable cursors patch

From
"Pavel Stehule"
Date:
2007/5/18, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au>:
> No. It works with scrollable cursors. It will work for cursors/selects
> which does not put the results in some store, such as WITH hold/group
> by/order by etc.... But most of these restrictions apply for normal
> 'Select for update' anyway. (With the order by clause, the
> implementation is as per the sql standards.)

some scrollable cursors are materialised. It depends on query :-(.
Simple query without join can works.

regards

Pavel

>
> I can update the documentation once the initial review is done and what
> I have done gets atleast a pass mark :-)
>
> Rgds,
> Arul Shaji
>
>
> Pavel Stehule wrote:
> > Hello
> >
> > I am not sure, but your solution will faill on scrollable cursors
> > (it's similar to holdable cursors). I miss part about limits in
> > documentation. Propably updatable cursors aren't supported by plpgsql
> > (and it's point to ToDo).
> >
> > Regards
> > Pavel Stehule
> >
> > 2007/5/17, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au>:
> >> Attached is an updated version of the updateable cursors patch against
> >> the latest cvs head.
> >>
> >> Most of the changes in the patch are to make it sync with the changes in
> >> CVS recently, for DECLARE CURSOR and EXPLAIN, as mentioned by Jaime in
> >> his mail below.
> >>
> >> Rgds,
> >> Arul Shaji
> >>
> >>
> >> FAST PostgreSQL wrote:
> >> > Right. I will send an updated patch against the CVS head in the next
> >> > couple of days.
> >> >
> >> > Jaime Casanova wrote:
> >> >> On 4/4/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
> >> >>> Attached is a working updateable cursors patch. The core
> >> >>> functionality has
> >> >>> been implemented and the patch also contains the regression tests and
> >> >>> documentation.
> >> >>>
> >> >>
> >> >> this one doesn't apply cleanly to HEAD because of the changes in
> >> >> http://archives.postgresql.org/pgsql-committers/2007-04/msg00447.php
> >> >>
> >> >
> >> >
> >> > ---------------------------(end of
> >> broadcast)---------------------------
> >> > TIP 6: explain analyze is your friend
> >> >
> >> >
> >>
> >>
> >> ---------------------------(end of broadcast)---------------------------
> >> TIP 7: You can help support the PostgreSQL project by donating at
> >>
> >>                 http://www.postgresql.org/about/donate
> >>
> >>
> >>
> >
> > ---------------------------(end of broadcast)---------------------------
> > TIP 3: Have you checked our extensive FAQ?
> >
> >               http://www.postgresql.org/docs/faq
> >
> >
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 1: if posting/reading through Usenet, please send an appropriate
>        subscribe-nomail command to majordomo@postgresql.org so that your
>        message can get through to the mailing list cleanly
>

Re: Updateable cursors patch

From
"FAST PostgreSQL"
Date:
Right. The current implementation allows only simple queries. Joins are
disallowed. According to the standard, updateable cursors cannot be
scrollable. So maybe I should put an explicit check during cursor
creation disallowing scrollable updateable cursors.

Rgds,
Arul Shaji


Pavel Stehule wrote:
> 2007/5/18, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au>:
>> No. It works with scrollable cursors. It will work for cursors/selects
>> which does not put the results in some store, such as WITH hold/group
>> by/order by etc.... But most of these restrictions apply for normal
>> 'Select for update' anyway. (With the order by clause, the
>> implementation is as per the sql standards.)
>
> some scrollable cursors are materialised. It depends on query :-(.
> Simple query without join can works.
>
> regards
>
> Pavel
>
>>
>> I can update the documentation once the initial review is done and what
>> I have done gets atleast a pass mark :-)
>>
>> Rgds,
>> Arul Shaji
>>
>>
>> Pavel Stehule wrote:
>> > Hello
>> >
>> > I am not sure, but your solution will faill on scrollable cursors
>> > (it's similar to holdable cursors). I miss part about limits in
>> > documentation. Propably updatable cursors aren't supported by plpgsql
>> > (and it's point to ToDo).
>> >
>> > Regards
>> > Pavel Stehule
>> >
>> > 2007/5/17, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au>:
>> >> Attached is an updated version of the updateable cursors patch against
>> >> the latest cvs head.
>> >>
>> >> Most of the changes in the patch are to make it sync with the
>> changes in
>> >> CVS recently, for DECLARE CURSOR and EXPLAIN, as mentioned by Jaime in
>> >> his mail below.
>> >>
>> >> Rgds,
>> >> Arul Shaji
>> >>
>> >>
>> >> FAST PostgreSQL wrote:
>> >> > Right. I will send an updated patch against the CVS head in the next
>> >> > couple of days.
>> >> >
>> >> > Jaime Casanova wrote:
>> >> >> On 4/4/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
>> >> >>> Attached is a working updateable cursors patch. The core
>> >> >>> functionality has
>> >> >>> been implemented and the patch also contains the regression
>> tests and
>> >> >>> documentation.
>> >> >>>
>> >> >>
>> >> >> this one doesn't apply cleanly to HEAD because of the changes in
>> >> >>
>> http://archives.postgresql.org/pgsql-committers/2007-04/msg00447.php
>> >> >>
>> >> >
>> >> >
>> >> > ---------------------------(end of
>> >> broadcast)---------------------------
>> >> > TIP 6: explain analyze is your friend
>> >> >
>> >> >
>> >>
>> >>
>> >> ---------------------------(end of
>> broadcast)---------------------------
>> >> TIP 7: You can help support the PostgreSQL project by donating at
>> >>
>> >>                 http://www.postgresql.org/about/donate
>> >>
>> >>
>> >>
>> >
>> > ---------------------------(end of
>> broadcast)---------------------------
>> > TIP 3: Have you checked our extensive FAQ?
>> >
>> >               http://www.postgresql.org/docs/faq
>> >
>> >
>>
>>
>> ---------------------------(end of broadcast)---------------------------
>> TIP 1: if posting/reading through Usenet, please send an appropriate
>>        subscribe-nomail command to majordomo@postgresql.org so that your
>>        message can get through to the mailing list cleanly
>>
>
>


Re: Updateable cursors patch

From
"FAST PostgreSQL"
Date:
Correction....  Meant to say

According to the standard, updateable cursors cannot be scrollable until
we have full cursor update.

FAST PostgreSQL wrote:
> Right. The current implementation allows only simple queries. Joins are
> disallowed. According to the standard, updateable cursors cannot be
> scrollable. So maybe I should put an explicit check during cursor
> creation disallowing scrollable updateable cursors.
>
> Rgds,
> Arul Shaji
>
>
> Pavel Stehule wrote:
>> 2007/5/18, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au>:
>>> No. It works with scrollable cursors. It will work for cursors/selects
>>> which does not put the results in some store, such as WITH hold/group
>>> by/order by etc.... But most of these restrictions apply for normal
>>> 'Select for update' anyway. (With the order by clause, the
>>> implementation is as per the sql standards.)
>>
>> some scrollable cursors are materialised. It depends on query :-(.
>> Simple query without join can works.
>>
>> regards
>>
>> Pavel
>>
>>>
>>> I can update the documentation once the initial review is done and what
>>> I have done gets atleast a pass mark :-)
>>>
>>> Rgds,
>>> Arul Shaji
>>>
>>>
>>> Pavel Stehule wrote:
>>> > Hello
>>> >
>>> > I am not sure, but your solution will faill on scrollable cursors
>>> > (it's similar to holdable cursors). I miss part about limits in
>>> > documentation. Propably updatable cursors aren't supported by plpgsql
>>> > (and it's point to ToDo).
>>> >
>>> > Regards
>>> > Pavel Stehule
>>> >
>>> > 2007/5/17, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au>:
>>> >> Attached is an updated version of the updateable cursors patch
>>> against
>>> >> the latest cvs head.
>>> >>
>>> >> Most of the changes in the patch are to make it sync with the
>>> changes in
>>> >> CVS recently, for DECLARE CURSOR and EXPLAIN, as mentioned by
>>> Jaime in
>>> >> his mail below.
>>> >>
>>> >> Rgds,
>>> >> Arul Shaji
>>> >>
>>> >>
>>> >> FAST PostgreSQL wrote:
>>> >> > Right. I will send an updated patch against the CVS head in the
>>> next
>>> >> > couple of days.
>>> >> >
>>> >> > Jaime Casanova wrote:
>>> >> >> On 4/4/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
>>> >> >>> Attached is a working updateable cursors patch. The core
>>> >> >>> functionality has
>>> >> >>> been implemented and the patch also contains the regression
>>> tests and
>>> >> >>> documentation.
>>> >> >>>
>>> >> >>
>>> >> >> this one doesn't apply cleanly to HEAD because of the changes in
>>> >> >>
>>> http://archives.postgresql.org/pgsql-committers/2007-04/msg00447.php
>>> >> >>
>>> >> >
>>> >> >
>>> >> > ---------------------------(end of
>>> >> broadcast)---------------------------
>>> >> > TIP 6: explain analyze is your friend
>>> >> >
>>> >> >
>>> >>
>>> >>
>>> >> ---------------------------(end of
>>> broadcast)---------------------------
>>> >> TIP 7: You can help support the PostgreSQL project by donating at
>>> >>
>>> >>                 http://www.postgresql.org/about/donate
>>> >>
>>> >>
>>> >>
>>> >
>>> > ---------------------------(end of
>>> broadcast)---------------------------
>>> > TIP 3: Have you checked our extensive FAQ?
>>> >
>>> >               http://www.postgresql.org/docs/faq
>>> >
>>> >
>>>
>>>
>>> ---------------------------(end of broadcast)---------------------------
>>> TIP 1: if posting/reading through Usenet, please send an appropriate
>>>        subscribe-nomail command to majordomo@postgresql.org so that your
>>>        message can get through to the mailing list cleanly
>>>
>>
>>
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 9: In versions below 8.0, the planner will ignore your desire to
>       choose an index scan if your joining column's datatypes do not
>       match
>
>


Re: Updateable cursors patch

From
"Pavel Stehule"
Date:
2007/5/18, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au>:
> Right. The current implementation allows only simple queries. Joins are
> disallowed. According to the standard, updateable cursors cannot be
> scrollable. So maybe I should put an explicit check during cursor
> creation disallowing scrollable updateable cursors.

I am for it. It good protection before strange bugs

Pavel

>
> Rgds,
> Arul Shaji
>
>
> Pavel Stehule wrote:
> > 2007/5/18, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au>:
> >> No. It works with scrollable cursors. It will work for cursors/selects
> >> which does not put the results in some store, such as WITH hold/group
> >> by/order by etc.... But most of these restrictions apply for normal
> >> 'Select for update' anyway. (With the order by clause, the
> >> implementation is as per the sql standards.)
> >
> > some scrollable cursors are materialised. It depends on query :-(.
> > Simple query without join can works.
> >
> > regards
> >
> > Pavel
> >
> >>
> >> I can update the documentation once the initial review is done and what
> >> I have done gets atleast a pass mark :-)
> >>
> >> Rgds,
> >> Arul Shaji
> >>
> >>
> >> Pavel Stehule wrote:
> >> > Hello
> >> >
> >> > I am not sure, but your solution will faill on scrollable cursors
> >> > (it's similar to holdable cursors). I miss part about limits in
> >> > documentation. Propably updatable cursors aren't supported by plpgsql
> >> > (and it's point to ToDo).
> >> >
> >> > Regards
> >> > Pavel Stehule
> >> >
> >> > 2007/5/17, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au>:
> >> >> Attached is an updated version of the updateable cursors patch against
> >> >> the latest cvs head.
> >> >>
> >> >> Most of the changes in the patch are to make it sync with the
> >> changes in
> >> >> CVS recently, for DECLARE CURSOR and EXPLAIN, as mentioned by Jaime in
> >> >> his mail below.
> >> >>
> >> >> Rgds,
> >> >> Arul Shaji
> >> >>
> >> >>
> >> >> FAST PostgreSQL wrote:
> >> >> > Right. I will send an updated patch against the CVS head in the next
> >> >> > couple of days.
> >> >> >
> >> >> > Jaime Casanova wrote:
> >> >> >> On 4/4/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
> >> >> >>> Attached is a working updateable cursors patch. The core
> >> >> >>> functionality has
> >> >> >>> been implemented and the patch also contains the regression
> >> tests and
> >> >> >>> documentation.
> >> >> >>>
> >> >> >>
> >> >> >> this one doesn't apply cleanly to HEAD because of the changes in
> >> >> >>
> >> http://archives.postgresql.org/pgsql-committers/2007-04/msg00447.php
> >> >> >>
> >> >> >
> >> >> >
> >> >> > ---------------------------(end of
> >> >> broadcast)---------------------------
> >> >> > TIP 6: explain analyze is your friend
> >> >> >
> >> >> >
> >> >>
> >> >>
> >> >> ---------------------------(end of
> >> broadcast)---------------------------
> >> >> TIP 7: You can help support the PostgreSQL project by donating at
> >> >>
> >> >>                 http://www.postgresql.org/about/donate
> >> >>
> >> >>
> >> >>
> >> >
> >> > ---------------------------(end of
> >> broadcast)---------------------------
> >> > TIP 3: Have you checked our extensive FAQ?
> >> >
> >> >               http://www.postgresql.org/docs/faq
> >> >
> >> >
> >>
> >>
> >> ---------------------------(end of broadcast)---------------------------
> >> TIP 1: if posting/reading through Usenet, please send an appropriate
> >>        subscribe-nomail command to majordomo@postgresql.org so that your
> >>        message can get through to the mailing list cleanly
> >>
> >
> >
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 9: In versions below 8.0, the planner will ignore your desire to
>        choose an index scan if your joining column's datatypes do not
>        match
>

Re: Updateable cursors patch

From
"Jaime Casanova"
Date:
On 5/17/07, Jaime Casanova <systemguards@gmail.com> wrote:
> On 5/17/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
> > No. It works with scrollable cursors. It will work for cursors/selects
> > which does not put the results in some store, such as WITH hold/group
> > by/order by etc.... But most of these restrictions apply for normal
> > 'Select for update' anyway. (With the order by clause, the
> > implementation is as per the sql standards.)
> >
>
> your patch doesn't work with updatable views because they don't have
> ctid columns....
>
> ERROR:  column "ctid" does not exist
> STATEMENT:  update vfoo set des_cta = des_cta || ' - prueba' where
> current of foo;
> ERROR:  current transaction is aborted, commands ignored until end of
> transaction block
>
> is this sane behavior? to accept create cursors for update on views
> and then failing to update "where current of" and rollback the entire
> transaction?
>
> comments?
>

maybe just send a better error message

--
regards,
Jaime Casanova

"Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs and the universe trying
to produce bigger and better idiots.
So far, the universe is winning."
                                       Richard Cook

Re: Updateable cursors patch

From
"FAST PostgreSQL"
Date:
 >
 > maybe just send a better error message

I do the transformation of a where current of clause into where clause
at the analyze stage itself(which is the right place to do. I think).
Hence this error. If I can postpone this transformation until after
re-write then the usual error will be thrown if there are no rules. It
is easily doable. But I want to confirm if this will break any other
part. I am looking into it now.

Rgds,
Arul Shaji


Jaime Casanova wrote:
> On 5/17/07, Jaime Casanova <systemguards@gmail.com> wrote:
>> On 5/17/07, FAST PostgreSQL <fastpgs@fast.fujitsu.com.au> wrote:
>> > No. It works with scrollable cursors. It will work for cursors/selects
>> > which does not put the results in some store, such as WITH hold/group
>> > by/order by etc.... But most of these restrictions apply for normal
>> > 'Select for update' anyway. (With the order by clause, the
>> > implementation is as per the sql standards.)
>> >
>>
>> your patch doesn't work with updatable views because they don't have
>> ctid columns....
>>
>> ERROR:  column "ctid" does not exist
>> STATEMENT:  update vfoo set des_cta = des_cta || ' - prueba' where
>> current of foo;
>> ERROR:  current transaction is aborted, commands ignored until end of
>> transaction block
>>
>> is this sane behavior? to accept create cursors for update on views
>> and then failing to update "where current of" and rollback the entire
>> transaction?
>>
>> comments?
>>
>
> maybe just send a better error message
>