pgsql.hackers
[Top] [All Lists]

Re: Patch: plan invalidation vs stored procedures

Subject: Re: Patch: plan invalidation vs stored procedures
From: Martin Pihlak
Date: Thu, 28 Aug 2008 12:32:14 +0300
Newsgroups: pgsql.hackers
Tom Lane wrote:
> I hadn't read it yet, but that makes it wrong already.  There's no need
> for any new inval traffic --- the existing syscache inval messages on
> pg_proc entries should serve fine.

Yes, creating a new message type was a bit short sighted -- attached is a patch
that uses syscache invalidation messages instead. This also adds additional
tupleId field to SharedInvalCatcacheMsg. This is used to identify the
invalidated tuple in PROC messages, for now others still pass InvalidOid.

> More generally, if we are to try to invalidate on the strength of
> pg_proc changes, what of other DDL changes?  Operators, operator
> classes, maybe?  How about renaming a schema?  I would like to see a
> line drawn between things we find worth trying to track and things we
> don't.  If there is no such line, we're going to need a patch a lot
> larger than this one.

The attached patch registers callbacks for namespace, operator and op family
catalog changes. PlanCacheCallback now takes catalog id as arg and can
take actions depending on the catalog type. Adding new catalogs is just a
matter of registering the callback in InitPlanCache. Of course, only tables
and functions have exact tracking -- other changes just invalidate all.

I'm wondering if the list of catalogs to be tracked should be fixed at all.
Maybe it would be better to call PlanCacheCallback directly on any syscache
entry invalidation? This way no catalog would be overlooked and the
cache_callback_list could be kept nice and short. PlanCacheCallback would
receive the catalog id and OID of the invalidated tuple and could then
decide whether it can do precise invalidation, flush the cache or just
skip the event. What do you think?

regards,
Martin

Index: src/backend/commands/prepare.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/prepare.c,v
retrieving revision 1.90
diff -c -r1.90 prepare.c
*** src/backend/commands/prepare.c      25 Aug 2008 22:42:32 -0000      1.90
--- src/backend/commands/prepare.c      28 Aug 2008 09:20:21 -0000
***************
*** 189,197 ****
        /* Shouldn't have a non-fully-planned plancache entry */
        if (!entry->plansource->fully_planned)
                elog(ERROR, "EXECUTE does not support unplanned prepared 
statements");
-       /* Shouldn't get any non-fixed-result cached plan, either */
-       if (!entry->plansource->fixed_result)
-               elog(ERROR, "EXECUTE does not support variable-result cached 
plans");
  
        /* Evaluate parameters, if any */
        if (entry->plansource->num_params > 0)
--- 189,194 ----
***************
*** 463,469 ****
                                                                  
cursor_options,
                                                                  stmt_list,
                                                                  true,
!                                                                 true);
  
        /* Now we can add entry to hash table */
        entry = (PreparedStatement *) hash_search(prepared_queries,
--- 460,466 ----
                                                                  
cursor_options,
                                                                  stmt_list,
                                                                  true,
!                                                                 false);
  
        /* Now we can add entry to hash table */
        entry = (PreparedStatement *) hash_search(prepared_queries,
***************
*** 524,534 ****
  TupleDesc
  FetchPreparedStatementResultDesc(PreparedStatement *stmt)
  {
!       /*
!        * Since we don't allow prepared statements' result tupdescs to change,
!        * there's no need for a revalidate call here.
!        */
!       Assert(stmt->plansource->fixed_result);
        if (stmt->plansource->resultDesc)
                return CreateTupleDescCopy(stmt->plansource->resultDesc);
        else
--- 521,529 ----
  TupleDesc
  FetchPreparedStatementResultDesc(PreparedStatement *stmt)
  {
!       /* Revalidate the plan to allow changes in tupdescs. */
!       RevalidateCachedPlan(stmt->plansource, false);
! 
        if (stmt->plansource->resultDesc)
                return CreateTupleDescCopy(stmt->plansource->resultDesc);
        else
***************
*** 650,658 ****
        /* Shouldn't have a non-fully-planned plancache entry */
        if (!entry->plansource->fully_planned)
                elog(ERROR, "EXPLAIN EXECUTE does not support unplanned 
prepared statements");
-       /* Shouldn't get any non-fixed-result cached plan, either */
-       if (!entry->plansource->fixed_result)
-               elog(ERROR, "EXPLAIN EXECUTE does not support variable-result 
cached plans");
  
        /* Replan if needed, and acquire a transient refcount */
        cplan = RevalidateCachedPlan(entry->plansource, true);
--- 645,650 ----
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.401
diff -c -r1.401 copyfuncs.c
*** src/backend/nodes/copyfuncs.c       22 Aug 2008 00:16:03 -0000      1.401
--- src/backend/nodes/copyfuncs.c       28 Aug 2008 09:20:21 -0000
***************
*** 84,89 ****
--- 84,90 ----
        COPY_NODE_FIELD(returningLists);
        COPY_NODE_FIELD(rowMarks);
        COPY_NODE_FIELD(relationOids);
+       COPY_NODE_FIELD(functionOids);
        COPY_SCALAR_FIELD(nParamExec);
  
        return newnode;
***************
*** 1882,1887 ****
--- 1883,1889 ----
        COPY_NODE_FIELD(limitCount);
        COPY_NODE_FIELD(rowMarks);
        COPY_NODE_FIELD(setOperations);
+       COPY_NODE_FIELD(functionOids);
  
        return newnode;
  }
Index: src/backend/optimizer/plan/planner.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v
retrieving revision 1.242
diff -c -r1.242 planner.c
*** src/backend/optimizer/plan/planner.c        17 Aug 2008 01:19:59 -0000      
1.242
--- src/backend/optimizer/plan/planner.c        28 Aug 2008 09:20:22 -0000
***************
*** 214,219 ****
--- 214,220 ----
        result->rowMarks = parse->rowMarks;
        result->relationOids = glob->relationOids;
        result->nParamExec = list_length(glob->paramlist);
+       result->functionOids = parse->functionOids;
  
        return result;
  }
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.377
diff -c -r1.377 analyze.c
*** src/backend/parser/analyze.c        25 Aug 2008 22:42:33 -0000      1.377
--- src/backend/parser/analyze.c        28 Aug 2008 09:20:23 -0000
***************
*** 226,231 ****
--- 226,234 ----
        result->querySource = QSRC_ORIGINAL;
        result->canSetTag = true;
  
+       /* Add the function oid list to Query */
+       result->functionOids = pstate->p_functionOids;
+ 
        return result;
  }
  
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/parse_func.c,v
retrieving revision 1.205
diff -c -r1.205 parse_func.c
*** src/backend/parser/parse_func.c     25 Aug 2008 22:42:33 -0000      1.205
--- src/backend/parser/parse_func.c     28 Aug 2008 09:20:23 -0000
***************
*** 323,328 ****
--- 323,332 ----
                                         parser_errposition(pstate, location)));
        }
  
+       /* add the function Oid to ParseState - this is later copied to Query */
+       if (!list_member_oid(pstate->p_functionOids, funcid))
+               pstate->p_functionOids = lappend_oid(pstate->p_functionOids, 
funcid);
+ 
        return retval;
  }
  
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.556
diff -c -r1.556 postgres.c
*** src/backend/tcop/postgres.c 19 Aug 2008 18:30:04 -0000      1.556
--- src/backend/tcop/postgres.c 28 Aug 2008 09:20:25 -0000
***************
*** 2177,2184 ****
                                         errmsg("unnamed prepared statement 
does not exist")));
        }
  
!       /* Prepared statements shouldn't have changeable result descs */
!       Assert(psrc->fixed_result);
  
        /*
         * If we are in aborted transaction state, we can't run
--- 2177,2184 ----
                                         errmsg("unnamed prepared statement 
does not exist")));
        }
  
!       /* Revalidate the plan to allow tupdesc changes */
!       RevalidateCachedPlan(psrc, true);
  
        /*
         * If we are in aborted transaction state, we can't run
Index: src/backend/utils/cache/catcache.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/cache/catcache.c,v
retrieving revision 1.144
diff -c -r1.144 catcache.c
*** src/backend/utils/cache/catcache.c  19 Jun 2008 00:46:05 -0000      1.144
--- src/backend/utils/cache/catcache.c  28 Aug 2008 09:20:25 -0000
***************
*** 1757,1763 ****
  void
  PrepareToInvalidateCacheTuple(Relation relation,
                                                          HeapTuple tuple,
!                                                       void (*function) (int, 
uint32, ItemPointer, Oid))
  {
        CatCache   *ccp;
        Oid                     reloid;
--- 1757,1763 ----
  void
  PrepareToInvalidateCacheTuple(Relation relation,
                                                          HeapTuple tuple,
!                                                       void (*function) (int, 
uint32, ItemPointer, Oid, Oid))
  {
        CatCache   *ccp;
        Oid                     reloid;
***************
*** 1794,1800 ****
                (*function) (ccp->id,
                                         CatalogCacheComputeTupleHashValue(ccp, 
tuple),
                                         &tuple->t_self,
!                                        ccp->cc_relisshared ? (Oid) 0 : 
MyDatabaseId);
        }
  }
  
--- 1794,1801 ----
                (*function) (ccp->id,
                                         CatalogCacheComputeTupleHashValue(ccp, 
tuple),
                                         &tuple->t_self,
!                                        ccp->cc_relisshared ? (Oid) 0 : 
MyDatabaseId,
!                                        (ccp->id == PROCOID) ? 
HeapTupleGetOid(tuple) : InvalidOid);
        }
  }
  
Index: src/backend/utils/cache/inval.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/cache/inval.c,v
retrieving revision 1.86
diff -c -r1.86 inval.c
*** src/backend/utils/cache/inval.c     19 Jun 2008 21:32:56 -0000      1.86
--- src/backend/utils/cache/inval.c     28 Aug 2008 09:20:26 -0000
***************
*** 307,319 ****
  static void
  AddCatcacheInvalidationMessage(InvalidationListHeader *hdr,
                                                           int id, uint32 
hashValue,
!                                                          ItemPointer 
tuplePtr, Oid dbId)
  {
        SharedInvalidationMessage msg;
  
        msg.cc.id = (int16) id;
        msg.cc.tuplePtr = *tuplePtr;
        msg.cc.dbId = dbId;
        msg.cc.hashValue = hashValue;
        AddInvalidationMessage(&hdr->cclist, &msg);
  }
--- 307,320 ----
  static void
  AddCatcacheInvalidationMessage(InvalidationListHeader *hdr,
                                                           int id, uint32 
hashValue,
!                                                          ItemPointer 
tuplePtr, Oid dbId, Oid tupleId)
  {
        SharedInvalidationMessage msg;
  
        msg.cc.id = (int16) id;
        msg.cc.tuplePtr = *tuplePtr;
        msg.cc.dbId = dbId;
+       msg.cc.tupleId = tupleId;
        msg.cc.hashValue = hashValue;
        AddInvalidationMessage(&hdr->cclist, &msg);
  }
***************
*** 414,423 ****
  RegisterCatcacheInvalidation(int cacheId,
                                                         uint32 hashValue,
                                                         ItemPointer tuplePtr,
!                                                        Oid dbId)
  {
        AddCatcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
!                                                                  cacheId, 
hashValue, tuplePtr, dbId);
  }
  
  /*
--- 415,424 ----
  RegisterCatcacheInvalidation(int cacheId,
                                                         uint32 hashValue,
                                                         ItemPointer tuplePtr,
!                                                        Oid dbId, Oid tupleId)
  {
        AddCatcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
!                                                                  cacheId, 
hashValue, tuplePtr, dbId, tupleId);
  }
  
  /*
***************
*** 489,495 ****
                                struct CACHECALLBACK *ccitem = 
cache_callback_list + i;
  
                                if (ccitem->id == msg->cc.id)
!                                       (*ccitem->function) (ccitem->arg, 
InvalidOid);
                        }
                }
        }
--- 490,496 ----
                                struct CACHECALLBACK *ccitem = 
cache_callback_list + i;
  
                                if (ccitem->id == msg->cc.id)
!                                       (*ccitem->function) (ccitem->arg, 
msg->cc.tupleId);
                        }
                }
        }
Index: src/backend/utils/cache/plancache.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/cache/plancache.c,v
retrieving revision 1.20
diff -c -r1.20 plancache.c
*** src/backend/utils/cache/plancache.c 25 Aug 2008 22:42:34 -0000      1.20
--- src/backend/utils/cache/plancache.c 28 Aug 2008 09:20:26 -0000
***************
*** 52,57 ****
--- 52,58 ----
  #include "utils/memutils.h"
  #include "utils/resowner.h"
  #include "utils/snapmgr.h"
+ #include "utils/syscache.h"
  
  
  typedef struct
***************
*** 81,87 ****
  static bool ScanQueryWalker(Node *node, ScanQueryWalkerContext *context);
  static bool rowmark_member(List *rowMarks, int rt_index);
  static bool plan_list_is_transient(List *stmt_list);
! static void PlanCacheCallback(Datum arg, Oid relid);
  static void InvalRelid(Oid relid, LOCKMODE lockmode,
                   InvalRelidContext *context);
  
--- 82,88 ----
  static bool ScanQueryWalker(Node *node, ScanQueryWalkerContext *context);
  static bool rowmark_member(List *rowMarks, int rt_index);
  static bool plan_list_is_transient(List *stmt_list);
! static void PlanCacheCallback(Datum arg, Oid objId);
  static void InvalRelid(Oid relid, LOCKMODE lockmode,
                   InvalRelidContext *context);
  
***************
*** 94,100 ****
  void
  InitPlanCache(void)
  {
!       CacheRegisterRelcacheCallback(PlanCacheCallback, (Datum) 0);
  }
  
  /*
--- 95,105 ----
  void
  InitPlanCache(void)
  {
!       CacheRegisterRelcacheCallback(PlanCacheCallback, Int32GetDatum(RELOID));
!       CacheRegisterSyscacheCallback(PROCOID, PlanCacheCallback, 
Int32GetDatum(PROCOID));
!       CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheCallback, 
Int32GetDatum(NAMESPACEOID));
!       CacheRegisterSyscacheCallback(OPEROID, PlanCacheCallback, 
Int32GetDatum(OPEROID));
!       CacheRegisterSyscacheCallback(OPFAMILYOID, PlanCacheCallback, 
Int32GetDatum(OPFAMILYOID));
  }
  
  /*
***************
*** 866,877 ****
   * PlanCacheCallback
   *            Relcache inval callback function
   *
!  * Invalidate all plans mentioning the given rel, or all plans mentioning
!  * any rel at all if relid == InvalidOid.
   */
  static void
! PlanCacheCallback(Datum arg, Oid relid)
  {
        ListCell   *lc1;
        ListCell   *lc2;
  
--- 871,883 ----
   * PlanCacheCallback
   *            Relcache inval callback function
   *
!  * Invalidate all plans mentioning the given OID, or all plans
!  * if objId == InvalidOid.
   */
  static void
! PlanCacheCallback(Datum arg, Oid objId)
  {
+       int                     catId = DatumGetInt32(arg);
        ListCell   *lc1;
        ListCell   *lc2;
  
***************
*** 883,925 ****
                /* No work if it's already invalidated */
                if (!plan || plan->dead)
                        continue;
-               if (plan->fully_planned)
-               {
-                       foreach(lc2, plan->stmt_list)
-                       {
-                               PlannedStmt *plannedstmt = (PlannedStmt *) 
lfirst(lc2);
  
!                               Assert(!IsA(plannedstmt, Query));
!                               if (!IsA(plannedstmt, PlannedStmt))
!                                       continue;       /* Ignore utility 
statements */
!                               if ((relid == InvalidOid) ? 
plannedstmt->relationOids != NIL :
!                                       
list_member_oid(plannedstmt->relationOids, relid))
!                               {
!                                       /* Invalidate the plan! */
!                                       plan->dead = true;
!                                       break;          /* out of stmt_list 
scan */
!                               }
!                       }
!               }
!               else
                {
!                       /*
!                        * For not-fully-planned entries we use 
ScanQueryForRelids, since
!                        * a recursive traversal is needed.  The callback API 
is a bit
!                        * tedious but avoids duplication of coding.
!                        */
!                       InvalRelidContext context;
  
!                       context.inval_relid = relid;
!                       context.plan = plan;
! 
!                       foreach(lc2, plan->stmt_list)
                        {
!                               Query      *query = (Query *) lfirst(lc2);
  
!                               Assert(IsA(query, Query));
!                               ScanQueryForRelids(query, InvalRelid, (void *) 
&context);
                        }
                }
        }
  }
--- 889,934 ----
                /* No work if it's already invalidated */
                if (!plan || plan->dead)
                        continue;
  
!               foreach(lc2, plan->stmt_list)
                {
!                       PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);
  
!                       Assert(!IsA(plannedstmt, Query));
!                       if (!IsA(plannedstmt, PlannedStmt))
!                               continue;       /* Ignore utility statements */
! 
!                       if (objId == InvalidOid)
!                               /* InvalidOid - invalidate all */
!                               plan->dead = true;
!                       else if (catId == PROCOID)
!                               /* Invalidate if we have this function */
!                               plan->dead = 
list_member_oid(plannedstmt->functionOids, objId);
!                       else if (catId == RELOID)
                        {
!                               /* Invalidate if we have this relation */
!                               if (plan->fully_planned)
!                                       plan->dead = 
list_member_oid(plannedstmt->relationOids, objId);
!                               else
!                               {
!                                       /*
!                                        * For not-fully-planned entries we use 
ScanQueryForRelids,
!                                        * since a recursive traversal is 
needed.  The callback API
!                                        * is a bit tedious but avoids 
duplication of coding.
!                                        */
!                                       InvalRelidContext context;
!                                       Query *query = (Query *) lfirst(lc2);
! 
!                                       context.inval_relid = objId;
!                                       context.plan = plan;
  
!                                       Assert(IsA(query, Query));
!                                       ScanQueryForRelids(query, InvalRelid, 
(void *) &context);
!                               }
                        }
+ 
+                       if (plan->dead)
+                               break;          /* out of stmt_list scan */
                }
        }
  }
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.371
diff -c -r1.371 parsenodes.h
*** src/include/nodes/parsenodes.h      7 Aug 2008 01:11:51 -0000       1.371
--- src/include/nodes/parsenodes.h      28 Aug 2008 09:20:28 -0000
***************
*** 132,137 ****
--- 132,139 ----
  
        Node       *setOperations;      /* set-operation tree if this is top 
level of
                                                                 * a 
UNION/INTERSECT/EXCEPT query */
+ 
+       List       *functionOids;       /* OIDs of functions the query 
references */
  } Query;
  
  
Index: src/include/nodes/plannodes.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/nodes/plannodes.h,v
retrieving revision 1.102
diff -c -r1.102 plannodes.h
*** src/include/nodes/plannodes.h       7 Aug 2008 19:35:02 -0000       1.102
--- src/include/nodes/plannodes.h       28 Aug 2008 09:20:28 -0000
***************
*** 72,77 ****
--- 72,79 ----
  
        List       *relationOids;       /* OIDs of relations the plan depends 
on */
  
+       List       *functionOids;       /* OIDs of functions the plan depends 
on */
+ 
        int                     nParamExec;             /* number of PARAM_EXEC 
Params used */
  } PlannedStmt;
  
Index: src/include/parser/parse_node.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/parser/parse_node.h,v
retrieving revision 1.54
diff -c -r1.54 parse_node.h
*** src/include/parser/parse_node.h     19 Jun 2008 00:46:06 -0000      1.54
--- src/include/parser/parse_node.h     28 Aug 2008 09:20:28 -0000
***************
*** 80,85 ****
--- 80,86 ----
        bool            p_is_update;
        Relation        p_target_relation;
        RangeTblEntry *p_target_rangetblentry;
+       List            *p_functionOids;
  } ParseState;
  
  extern ParseState *make_parsestate(ParseState *parentParseState);
Index: src/include/storage/sinval.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/storage/sinval.h,v
retrieving revision 1.48
diff -c -r1.48 sinval.h
*** src/include/storage/sinval.h        19 Jun 2008 21:32:56 -0000      1.48
--- src/include/storage/sinval.h        28 Aug 2008 09:20:28 -0000
***************
*** 54,59 ****
--- 54,60 ----
        int16           id;                             /* cache ID --- must be 
first */
        ItemPointerData tuplePtr;       /* tuple identifier in cached relation 
*/
        Oid                     dbId;                   /* database ID, or 0 if 
a shared relation */
+       Oid                     tupleId;                /* OID of the 
invalidated tuple */
        uint32          hashValue;              /* hash value of key for this 
catcache */
  } SharedInvalCatcacheMsg;
  
Index: src/include/utils/catcache.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/utils/catcache.h,v
retrieving revision 1.67
diff -c -r1.67 catcache.h
*** src/include/utils/catcache.h        19 Jun 2008 00:46:06 -0000      1.67
--- src/include/utils/catcache.h        28 Aug 2008 09:20:28 -0000
***************
*** 184,190 ****
                                                 ItemPointer pointer);
  extern void PrepareToInvalidateCacheTuple(Relation relation,
                                                          HeapTuple tuple,
!                                                  void (*function) (int, 
uint32, ItemPointer, Oid));
  
  extern void PrintCatCacheLeakWarning(HeapTuple tuple);
  extern void PrintCatCacheListLeakWarning(CatCList *list);
--- 184,190 ----
                                                 ItemPointer pointer);
  extern void PrepareToInvalidateCacheTuple(Relation relation,
                                                          HeapTuple tuple,
!                                                  void (*function) (int, 
uint32, ItemPointer, Oid, Oid));
  
  extern void PrintCatCacheLeakWarning(HeapTuple tuple);
  extern void PrintCatCacheListLeakWarning(CatCList *list);
Index: src/test/regress/expected/plancache.out
===================================================================
RCS file: /projects/cvsroot/pgsql/src/test/regress/expected/plancache.out,v
retrieving revision 1.7
diff -c -r1.7 plancache.out
*** src/test/regress/expected/plancache.out     8 Jun 2008 22:41:04 -0000       
1.7
--- src/test/regress/expected/plancache.out     28 Aug 2008 09:20:29 -0000
***************
*** 53,61 ****
  -- since clients probably aren't expecting that to change on the fly
  ALTER TABLE pcachetest ADD COLUMN q3 bigint;
  EXECUTE prepstmt;
! ERROR:  cached plan must not change result type
  EXECUTE prepstmt2(123);
! ERROR:  cached plan must not change result type
  -- but we're nice guys and will let you undo your mistake
  ALTER TABLE pcachetest DROP COLUMN q3;
  EXECUTE prepstmt;
--- 53,74 ----
  -- since clients probably aren't expecting that to change on the fly
  ALTER TABLE pcachetest ADD COLUMN q3 bigint;
  EXECUTE prepstmt;
!         q1        |        q2         | q3 
! ------------------+-------------------+----
!  4567890123456789 | -4567890123456789 |   
!  4567890123456789 |               123 |   
!               123 |               456 |   
!               123 |  4567890123456789 |   
!  4567890123456789 |  4567890123456789 |   
! (5 rows)
! 
  EXECUTE prepstmt2(123);
!  q1  |        q2        | q3 
! -----+------------------+----
!  123 |              456 |   
!  123 | 4567890123456789 |   
! (2 rows)
! 
  -- but we're nice guys and will let you undo your mistake
  ALTER TABLE pcachetest DROP COLUMN q3;
  EXECUTE prepstmt;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@xxxxxxxxxxxxxx)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
<Prev in Thread] Current Thread [Next in Thread>
Privacy Policy