Thread: Assertion failure with a subtransaction and cursor

Assertion failure with a subtransaction and cursor

From
Heikki Linnakangas
Date:
On all versions starting from 8.0 where subtransactions were introduced,
this causes an assertion failure:

postgres=# BEGIN;
BEGIN
postgres=# DECLARE foocur CURSOR FOR SELECT a FROM
generate_series(1,500000) a;
DECLARE CURSOR
postgres=# SAVEPOINT sp;
SAVEPOINT
postgres=# FETCH foocur;
 a
───
 1
(1 row)

postgres=# error; -- cause subxact abort
ERROR:  syntax error at or near "error"
LINE 1: error;
        ^
postgres=# ROLLBACK TO SAVEPOINT sp;
ROLLBACK
postgres=# FETCH ALL FROM foocur;
server closed the connection unexpectedly
    This probably means the server terminated abnormally
    before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.

In server log:

TRAP: FailedAssertion("!(((file) > 0 && (file) < (int) SizeVfdCache &&
VfdCache[file].fileName != ((void *)0)))", File: "fd.c", Line: 1101)

When the first row is fetched from the cursor, the function scan is
materialized in a temporaray file. At the error, the temporary file is
deleted. But the FETCH ALL command expects the cursor to be still open,
and the file to exist, so it fails.

If assertions are disabled, you get an "ERROR:  unexpected end of tape"
error instead.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com

Re: Assertion failure with a subtransaction and cursor

From
Tom Lane
Date:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
> On all versions starting from 8.0 where subtransactions were introduced,
> this causes an assertion failure:

Ugh :-(

This ties into the more general issue that it's not clear what effect a
subtransaction rollback should have on a cursor.  You could argue that
ideally the cursor should revert to its pre-savepoint state.  We didn't
implement that in 8.0 because it seemed too hard, but this bug shows
that not rolling back the cursor isn't exactly easy either.

Not sure what to do.  The only fix that seems bulletproof at the moment
is to declare that any cursor that's been touched at all in a
subtransaction is marked "broken" if the subtransaction rolls back.
That might be too great a loss of functionality.  It would block off
the controversial aspects of behavior though ...

            regards, tom lane

Re: Assertion failure with a subtransaction and cursor

From
Heikki Linnakangas
Date:
Tom Lane wrote:
> Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
>> On all versions starting from 8.0 where subtransactions were introduced,
>> this causes an assertion failure:
>
> Ugh :-(
>
> This ties into the more general issue that it's not clear what effect a
> subtransaction rollback should have on a cursor.  You could argue that
> ideally the cursor should revert to its pre-savepoint state.  We didn't
> implement that in 8.0 because it seemed too hard, but this bug shows
> that not rolling back the cursor isn't exactly easy either.

Yeah, the current behavior is debatable. But it's quite sane, useful and
well-defined as it is, so I don't feel any urge to change it.

> Not sure what to do.  The only fix that seems bulletproof at the moment
> is to declare that any cursor that's been touched at all in a
> subtransaction is marked "broken" if the subtransaction rolls back.
> That might be too great a loss of functionality.  It would block off
> the controversial aspects of behavior though ...

Hmm, I think we should track temporary files using resource owners.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com

Re: Assertion failure with a subtransaction and cursor

From
Tom Lane
Date:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
> Tom Lane wrote:
>> Not sure what to do.  The only fix that seems bulletproof at the moment
>> is to declare that any cursor that's been touched at all in a
>> subtransaction is marked "broken" if the subtransaction rolls back.
>> That might be too great a loss of functionality.  It would block off
>> the controversial aspects of behavior though ...

> Hmm, I think we should track temporary files using resource owners.

That would probably be a workable solution if temp files are the only
problem.  What I'm afraid of is that this type of problem exists
*everywhere* that we track the need for cleanup operations using the
assumption that subtransactions are nested.  If that's the case then we
are looking at a very major rewrite to make things bulletproof --- much
larger than I'd feel comfortable back-patching, especially so far back
as 8.0.  I'm thinking we might have little choice but to disable the
functionality in back branches.

            regards, tom lane

Re: Assertion failure with a subtransaction and cursor

From
Alvaro Herrera
Date:
Tom Lane wrote:
> Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
> > Tom Lane wrote:
> >> Not sure what to do.  The only fix that seems bulletproof at the moment
> >> is to declare that any cursor that's been touched at all in a
> >> subtransaction is marked "broken" if the subtransaction rolls back.
> >> That might be too great a loss of functionality.  It would block off
> >> the controversial aspects of behavior though ...
>
> > Hmm, I think we should track temporary files using resource owners.
>
> That would probably be a workable solution if temp files are the only
> problem.  What I'm afraid of is that this type of problem exists
> *everywhere* that we track the need for cleanup operations using the
> assumption that subtransactions are nested.  If that's the case then we
> are looking at a very major rewrite to make things bulletproof --- much
> larger than I'd feel comfortable back-patching, especially so far back
> as 8.0.  I'm thinking we might have little choice but to disable the
> functionality in back branches.

Hmm, the reason we didn't disable it is that people requested it
explicitely.  In fact IIRC we had this very same discussion years ago,
except without the crashing test case.  I fear that if it gets disabled,
some people will be upset.

--
Alvaro Herrera                                http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.

Re: Assertion failure with a subtransaction and cursor

From
Tom Lane
Date:
I wrote:
> Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
>> Hmm, I think we should track temporary files using resource owners.

> That would probably be a workable solution if temp files are the only
> problem.  What I'm afraid of is that this type of problem exists
> *everywhere* that we track the need for cleanup operations using the
> assumption that subtransactions are nested.

I just spent a tedious hour digging through every function called by
AbortSubTransaction(), and came to the conclusion that this fear may be
overblown, so long as we are willing to stipulate that user-visible
side effects caused by a cursor's query are to be rolled back if they
occur during a subtransaction that is rolled back.  From the user's
perspective this may make things a bit unpredictable, since it is not
always clear exactly when a side effect will occur during the execution
of a query.  However it doesn't seem like it can actually break the
system.

(At least not for code in core CVS.  Outside modules might be doing
unsafe things in RegisterSubXactCallback callbacks.  But that's not
our responsibility to fix.)

It might be a good idea to forbid writeable CTEs in cursor queries,
because that would expose the unpredictability a lot more, and maybe
open some internal issues too -- I noted snapshot management as a likely
problem if the executor is allowed to create its own snapshots.  But as
far as what's in CVS is concerned that's not an issue yet anyhow.  If
we did want to allow it, we could probably make it safe by forcing all
the writable CTEs to run at cursor creation time.

So as far as I can tell at the moment, temp files really are the only
problem, and making them be managed by resource owners instead of a
subxact-based release policy should fix that.  I can go work on that,
unless you wanted to?

            regards, tom lane

Re: Assertion failure with a subtransaction and cursor

From
Heikki Linnakangas
Date:
Tom Lane wrote:
> So as far as I can tell at the moment, temp files really are the only
> problem, and making them be managed by resource owners instead of a
> subxact-based release policy should fix that.

Ok, good.

> I can go work on that, unless you wanted to?

I started hacking on that when I posted, so I can finish it.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com

Re: Assertion failure with a subtransaction and cursor

From
Tom Lane
Date:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
> Tom Lane wrote:
>> I can go work on that, unless you wanted to?

> I started hacking on that when I posted, so I can finish it.

Sounds good.  I added a bit to the ROLLBACK TO reference page to remind
us what we think the behavior is supposed to be for cursor rollback.

            regards, tom lane

Re: Assertion failure with a subtransaction and cursor

From
Heikki Linnakangas
Date:
Heikki Linnakangas wrote:
> Tom Lane wrote:
>> So as far as I can tell at the moment, temp files really are the only
>> problem, and making them be managed by resource owners instead of a
>> subxact-based release policy should fix that.
>
> Ok, good.
>
>> I can go work on that, unless you wanted to?
>
> I started hacking on that when I posted, so I can finish it.

So, here's what I got this far, which is pretty straightforward.
Temporary files not opened in the interXact mode are registered with
CurrentResourceOwner, ensuring they're cleaned up at at the end of
(sub)transaction, or when the portal is closed if it's a temporary file
related to a cursor.

The logical next step would be to get rid of the interXact concept
altogether and always associate temporary files with the current
resource owner; the caller should switch to a sufficiently long-lived
one before calling. But I'm hesitant to change the signature of
OpenTemporaryFile, and tuplestore_begin_heap doesn't need the interXact
argument anymore either. Changing the signature of tuplestore_begin_heap
would certainly break user-defined set-returning C functions. So at
least in back-branches, perhaps we should leave those as dummy arguments
and elog(ERROR) if interXact is not passed in as false.

Google turned up one extra module that calls tuplestore_begin_heap with
interXact set to 'true', but frankly that one looks broken to me. It
should be using 'false':
http://cvs.pgfoundry.org/cgi-bin/cvsweb.cgi/python/be/src/call/pl.c?rev=1.20&content-type=text/x-cvsweb-markup

I left the cleanup of files/dirs opened with AllocateFile/Dir alone,
although it looks a bit inconsistent now that they don't use resource
owners as well.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com
Index: src/backend/storage/file/fd.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/storage/file/fd.c,v
retrieving revision 1.150
diff -c -r1.150 fd.c
*** src/backend/storage/file/fd.c    5 Aug 2009 18:01:54 -0000    1.150
--- src/backend/storage/file/fd.c    2 Dec 2009 21:42:01 -0000
***************
*** 55,60 ****
--- 55,61 ----
  #include "storage/fd.h"
  #include "storage/ipc.h"
  #include "utils/guc.h"
+ #include "utils/resowner.h"


  /*
***************
*** 122,140 ****

  /* these are the assigned bits in fdstate below: */
  #define FD_TEMPORARY        (1 << 0)    /* T = delete when closed */
- #define FD_XACT_TEMPORARY    (1 << 1)    /* T = delete at eoXact */
-
- /*
-  * Flag to tell whether it's worth scanning VfdCache looking for temp files to
-  * close
-  */
- static bool have_xact_temporary_files = false;

  typedef struct vfd
  {
      int            fd;                /* current FD, or VFD_CLOSED if none */
      unsigned short fdstate;        /* bitflags for VFD's state */
!     SubTransactionId create_subid;        /* for TEMPORARY fds, creating subxact */
      File        nextFree;        /* link to next free VFD, if in freelist */
      File        lruMoreRecently;    /* doubly linked recency-of-use list */
      File        lruLessRecently;
--- 123,134 ----

  /* these are the assigned bits in fdstate below: */
  #define FD_TEMPORARY        (1 << 0)    /* T = delete when closed */

  typedef struct vfd
  {
      int            fd;                /* current FD, or VFD_CLOSED if none */
      unsigned short fdstate;        /* bitflags for VFD's state */
!     ResourceOwner resowner;        /* owner, for automatic cleanup */
      File        nextFree;        /* link to next free VFD, if in freelist */
      File        lruMoreRecently;    /* doubly linked recency-of-use list */
      File        lruLessRecently;
***************
*** 865,870 ****
--- 859,865 ----
      vfdP->fileMode = fileMode;
      vfdP->seekPos = 0;
      vfdP->fdstate = 0x0;
+     vfdP->resowner = NULL;

      return file;
  }
***************
*** 876,883 ****
   * There's no need to pass in fileFlags or fileMode either, since only
   * one setting makes any sense for a temp file.
   *
!  * interXact: if true, don't close the file at end-of-transaction. In
!  * most cases, you don't want temporary files to outlive the transaction
   * that created them, so this should be false -- but if you need
   * "somewhat" temporary storage, this might be useful. In either case,
   * the file is removed when the File is explicitly closed.
--- 871,878 ----
   * There's no need to pass in fileFlags or fileMode either, since only
   * one setting makes any sense for a temp file.
   *
!  * interXact: if true, don't associate the file with CurrentResourceOwner.
!  * In most cases, you don't want temporary files to outlive the transaction
   * that created them, so this should be false -- but if you need
   * "somewhat" temporary storage, this might be useful. In either case,
   * the file is removed when the File is explicitly closed.
***************
*** 918,931 ****
      /* Mark it for deletion at close */
      VfdCache[file].fdstate |= FD_TEMPORARY;

!     /* Mark it for deletion at EOXact */
      if (!interXact)
      {
!         VfdCache[file].fdstate |= FD_XACT_TEMPORARY;
!         VfdCache[file].create_subid = GetCurrentSubTransactionId();
!
!         /* ensure cleanup happens at eoxact */
!         have_xact_temporary_files = true;
      }

      return file;
--- 913,924 ----
      /* Mark it for deletion at close */
      VfdCache[file].fdstate |= FD_TEMPORARY;

!     /* Associate it with the current resource owner */
      if (!interXact)
      {
!         ResourceOwnerEnlargeFiles(CurrentResourceOwner);
!         ResourceOwnerRememberFile(CurrentResourceOwner, file);
!         VfdCache[file].resowner = CurrentResourceOwner;
      }

      return file;
***************
*** 1051,1056 ****
--- 1044,1052 ----
              elog(LOG, "could not unlink file \"%s\": %m", vfdP->fileName);
      }

+     if (vfdP->resowner)
+         ResourceOwnerForgetFile(vfdP->resowner, file);
+
      /*
       * Return the Vfd slot to the free list
       */
***************
*** 1695,1718 ****
  {
      Index        i;

-     if (have_xact_temporary_files)
-     {
-         Assert(FileIsNotOpen(0));        /* Make sure ring not corrupted */
-         for (i = 1; i < SizeVfdCache; i++)
-         {
-             unsigned short fdstate = VfdCache[i].fdstate;
-
-             if ((fdstate & FD_XACT_TEMPORARY) &&
-                 VfdCache[i].create_subid == mySubid)
-             {
-                 if (isCommit)
-                     VfdCache[i].create_subid = parentSubid;
-                 else if (VfdCache[i].fileName != NULL)
-                     FileClose(i);
-             }
-         }
-     }
-
      for (i = 0; i < numAllocatedDescs; i++)
      {
          if (allocatedDescs[i].create_subid == mySubid)
--- 1691,1696 ----
***************
*** 1732,1746 ****
   * AtEOXact_Files
   *
   * This routine is called during transaction commit or abort (it doesn't
!  * particularly care which).  All still-open per-transaction temporary file
!  * VFDs are closed, which also causes the underlying files to be
!  * deleted. Furthermore, all "allocated" stdio files are closed.
   * We also forget any transaction-local temp tablespace list.
   */
  void
  AtEOXact_Files(void)
  {
!     CleanupTempFiles(false);
      tempTableSpaces = NULL;
      numTempTableSpaces = -1;
  }
--- 1710,1725 ----
   * AtEOXact_Files
   *
   * This routine is called during transaction commit or abort (it doesn't
!  * particularly care which).  All "allocated" stdio files are closed.
   * We also forget any transaction-local temp tablespace list.
   */
  void
  AtEOXact_Files(void)
  {
!     /* Clean up "allocated" stdio files and dirs. */
!     while (numAllocatedDescs > 0)
!         FreeDesc(&allocatedDescs[0]);
!
      tempTableSpaces = NULL;
      numTempTableSpaces = -1;
  }
***************
*** 1749,1802 ****
   * AtProcExit_Files
   *
   * on_proc_exit hook to clean up temp files during backend shutdown.
!  * Here, we want to clean up *all* temp files including interXact ones.
   */
  static void
  AtProcExit_Files(int code, Datum arg)
  {
!     CleanupTempFiles(true);
! }
!
! /*
!  * Close temporary files and delete their underlying files.
!  *
!  * isProcExit: if true, this is being called as the backend process is
!  * exiting. If that's the case, we should remove all temporary files; if
!  * that's not the case, we are being called for transaction commit/abort
!  * and should only remove transaction-local temp files.  In either case,
!  * also clean up "allocated" stdio files and dirs.
!  */
! static void
! CleanupTempFiles(bool isProcExit)
! {
!     Index        i;

      /*
!      * Careful here: at proc_exit we need extra cleanup, not just
!      * xact_temporary files.
       */
!     if (isProcExit || have_xact_temporary_files)
      {
!         Assert(FileIsNotOpen(0));        /* Make sure ring not corrupted */
!         for (i = 1; i < SizeVfdCache; i++)
!         {
!             unsigned short fdstate = VfdCache[i].fdstate;
!
!             if ((fdstate & FD_TEMPORARY) && VfdCache[i].fileName != NULL)
!             {
!                 /*
!                  * If we're in the process of exiting a backend process, close
!                  * all temporary files. Otherwise, only close temporary files
!                  * local to the current transaction.
!                  */
!                 if (isProcExit || (fdstate & FD_XACT_TEMPORARY))
!                     FileClose(i);
!             }
!         }

!         have_xact_temporary_files = false;
      }

      while (numAllocatedDescs > 0)
          FreeDesc(&allocatedDescs[0]);
  }
--- 1728,1755 ----
   * AtProcExit_Files
   *
   * on_proc_exit hook to clean up temp files during backend shutdown.
!  * Here, we want to clean up *all* temp files and "allocated" stdio files
!  * and dirs.
   */
  static void
  AtProcExit_Files(int code, Datum arg)
  {
!     int i;

      /*
!      * Close all still-open temporary file VFD, which also causes the
!      * underlying files to be deleted.
       */
!     Assert(FileIsNotOpen(0));        /* Make sure ring not corrupted */
!     for (i = 1; i < SizeVfdCache; i++)
      {
!         unsigned short fdstate = VfdCache[i].fdstate;

!         if ((fdstate & FD_TEMPORARY) && VfdCache[i].fileName != NULL)
!             FileClose(i);
      }

+     /* Clean up "allocated" stdio files and dirs. */
      while (numAllocatedDescs > 0)
          FreeDesc(&allocatedDescs[0]);
  }
Index: src/backend/utils/resowner/resowner.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/resowner/resowner.c,v
retrieving revision 1.32
diff -c -r1.32 resowner.c
*** src/backend/utils/resowner/resowner.c    11 Jun 2009 14:49:06 -0000    1.32
--- src/backend/utils/resowner/resowner.c    2 Dec 2009 21:42:01 -0000
***************
*** 72,77 ****
--- 72,82 ----
      int            nsnapshots;        /* number of owned snapshot references */
      Snapshot   *snapshots;        /* dynamically allocated array */
      int            maxsnapshots;    /* currently allocated array size */
+
+     /* We have built-in support for remembering open temporary files */
+     int            nfiles;            /* number of owned temporary files */
+     File       *files;            /* dynamically allocated array */
+     int            maxfiles;        /* currently allocated array size */
  } ResourceOwnerData;


***************
*** 105,110 ****
--- 110,116 ----
  static void PrintPlanCacheLeakWarning(CachedPlan *plan);
  static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
  static void PrintSnapshotLeakWarning(Snapshot snapshot);
+ static void PrintFileLeakWarning(File file);


  /*****************************************************************************
***************
*** 316,321 ****
--- 322,335 ----
              UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
          }

+         /* Ditto for temporary files */
+         while (owner->nfiles > 0)
+         {
+             if (isCommit)
+                 PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
+             FileClose(owner->files[owner->nfiles - 1]);
+         }
+
          /* Clean up index scans too */
          ReleaseResources_hash();
      }
***************
*** 347,352 ****
--- 361,367 ----
      Assert(owner->nplanrefs == 0);
      Assert(owner->ntupdescs == 0);
      Assert(owner->nsnapshots == 0);
+     Assert(owner->nfiles == 0);

      /*
       * Delete children.  The recursive call will delink the child from me, so
***************
*** 377,382 ****
--- 392,399 ----
          pfree(owner->tupdescs);
      if (owner->snapshots)
          pfree(owner->snapshots);
+     if (owner->files)
+         pfree(owner->files);

      pfree(owner);
  }
***************
*** 1035,1037 ****
--- 1052,1138 ----
           "Snapshot reference leak: Snapshot %p still referenced",
           snapshot);
  }
+
+
+ /*
+  * Make sure there is room for at least one more entry in a ResourceOwner's
+  * files reference array.
+  *
+  * This is separate from actually inserting an entry because if we run out
+  * of memory, it's critical to do so *before* acquiring the resource.
+  */
+ void
+ ResourceOwnerEnlargeFiles(ResourceOwner owner)
+ {
+     int            newmax;
+
+     if (owner->nfiles < owner->maxfiles)
+         return;                    /* nothing to do */
+
+     if (owner->files == NULL)
+     {
+         newmax = 16;
+         owner->files = (File *)
+             MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File));
+         owner->maxfiles = newmax;
+     }
+     else
+     {
+         newmax = owner->maxfiles * 2;
+         owner->files = (File *)
+             repalloc(owner->files, newmax * sizeof(File));
+         owner->maxfiles = newmax;
+     }
+ }
+
+ /*
+  * Remember that a temporary file is owned by a ResourceOwner
+  *
+  * Caller must have previously done ResourceOwnerEnlargeFiles()
+  */
+ void
+ ResourceOwnerRememberFile(ResourceOwner owner, File file)
+ {
+     Assert(owner->nfiles < owner->maxfiles);
+     owner->files[owner->nfiles] = file;
+     owner->nfiles++;
+ }
+
+ /*
+  * Forget that a temporary file is owned by a ResourceOwner
+  */
+ void
+ ResourceOwnerForgetFile(ResourceOwner owner, File file)
+ {
+     File       *files = owner->files;
+     int            ns1 = owner->nfiles - 1;
+     int            i;
+
+     for (i = ns1; i >= 0; i--)
+     {
+         if (files[i] == file)
+         {
+             while (i < ns1)
+             {
+                 files[i] = files[i + 1];
+                 i++;
+             }
+             owner->nfiles = ns1;
+             return;
+         }
+     }
+     elog(ERROR, "temporery file %d is not owned by resource owner %s",
+          file, owner->name);
+ }
+
+
+ /*
+  * Debugging subroutine
+  */
+ static void
+ PrintFileLeakWarning(File file)
+ {
+     elog(WARNING,
+          "Temporary file leak: File %d still referenced",
+          file);
+ }
Index: src/include/utils/resowner.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/utils/resowner.h,v
retrieving revision 1.17
diff -c -r1.17 resowner.h
*** src/include/utils/resowner.h    1 Jan 2009 17:24:02 -0000    1.17
--- src/include/utils/resowner.h    2 Dec 2009 21:42:01 -0000
***************
*** 20,25 ****
--- 20,26 ----
  #define RESOWNER_H

  #include "storage/buf.h"
+ #include "storage/fd.h"
  #include "utils/catcache.h"
  #include "utils/plancache.h"
  #include "utils/snapshot.h"
***************
*** 129,132 ****
--- 130,140 ----
  extern void ResourceOwnerForgetSnapshot(ResourceOwner owner,
                              Snapshot snapshot);

+ /* support for temporary file management */
+ extern void ResourceOwnerEnlargeFiles(ResourceOwner owner);
+ extern void ResourceOwnerRememberFile(ResourceOwner owner,
+                           File file);
+ extern void ResourceOwnerForgetFile(ResourceOwner owner,
+                         File file);
+
  #endif   /* RESOWNER_H */

Re: Assertion failure with a subtransaction and cursor

From
Tom Lane
Date:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
> The logical next step would be to get rid of the interXact concept
> altogether and always associate temporary files with the current
> resource owner; the caller should switch to a sufficiently long-lived
> one before calling.

That would mean having a session-lifespan resource owner, which doesn't
seem amazingly useful to me: we have no other resource types for which
that would be even a little bit sane.  I'm inclined to leave the temp
file API as nearly as possible as-is.  Having it attach only xact-local
files to the CurrentResourceOwner sounds fine to me.

One thought here is that my inclination would be to keep
CleanupTemporaryFiles in place and have it just look for temp files that
didn't get closed, as a debugging aid.  This is comparable to the
behavior of other modules that are mostly driven by resource managers.
We didn't take out the end-of-xact checks when we added resource manager
support elsewhere, and I'm not inclined to do that here.  I agree we
don't need an EOSubXact-time crosscheck, though.

Other than that quibble it looks sane from here.

            regards, tom lane

Re: Assertion failure with a subtransaction and cursor

From
Heikki Linnakangas
Date:
Tom Lane wrote:
> Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
>> The logical next step would be to get rid of the interXact concept
>> altogether and always associate temporary files with the current
>> resource owner; the caller should switch to a sufficiently long-lived
>> one before calling.
>
> That would mean having a session-lifespan resource owner, which doesn't
> seem amazingly useful to me: we have no other resource types for which
> that would be even a little bit sane.

The only user of "interXact == true" is when the result set of a
holdable cursor is materialized. I'm envisioning a new resource owner
alongside PortalData.holdContext which is the memory context used to
hold the tuplestore.

>  I'm inclined to leave the temp
> file API as nearly as possible as-is.  Having it attach only xact-local
> files to the CurrentResourceOwner sounds fine to me.

Ok, fine with me.

> One thought here is that my inclination would be to keep
> CleanupTemporaryFiles in place and have it just look for temp files that
> didn't get closed, as a debugging aid.  This is comparable to the
> behavior of other modules that are mostly driven by resource managers.
> We didn't take out the end-of-xact checks when we added resource manager
> support elsewhere, and I'm not inclined to do that here.  I agree we
> don't need an EOSubXact-time crosscheck, though.

Ok, committed that way. I made the cross-check a WARNING. That seems
like the right level of seriousness, although I see that many of the
other similar checks are Asserts.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com

Re: Assertion failure with a subtransaction and cursor

From
Tom Lane
Date:
Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
> Ok, committed that way. I made the cross-check a WARNING. That seems
> like the right level of seriousness, although I see that many of the
> other similar checks are Asserts.

Looks good.  I'm a bit tempted to rename the interXact argument to
something like noOwner --- no change in API, just name it a bit closer
to what it actually does now.  Thoughts?

            regards, tom lane

Re: Assertion failure with a subtransaction and cursor

From
Heikki Linnakangas
Date:
Tom Lane wrote:
> Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:
>> Ok, committed that way. I made the cross-check a WARNING. That seems
>> like the right level of seriousness, although I see that many of the
>> other similar checks are Asserts.
>
> Looks good.  I'm a bit tempted to rename the interXact argument to
> something like noOwner --- no change in API, just name it a bit closer
> to what it actually does now.  Thoughts?

Well, I did keep the cross-check at end-of-transaction, so it's not a
complete misnomer as it is; a file opened with interXact=false can't be
used across transactions. I won't object if you feel like renaming it,
though.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com