]> granicus.if.org Git - postgresql/commitdiff
Support unlogged tables.
authorRobert Haas <rhaas@postgresql.org>
Wed, 29 Dec 2010 11:48:53 +0000 (06:48 -0500)
committerRobert Haas <rhaas@postgresql.org>
Wed, 29 Dec 2010 11:48:53 +0000 (06:48 -0500)
The contents of an unlogged table are WAL-logged; thus, they are not
available on standby servers and are truncated whenever the database
system enters recovery.  Indexes on unlogged tables are also unlogged.
Unlogged GiST indexes are not currently supported.

49 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/indexam.sgml
doc/src/sgml/ref/create_table.sgml
doc/src/sgml/ref/create_table_as.sgml
doc/src/sgml/ref/pg_dump.sgml
doc/src/sgml/ref/pg_dumpall.sgml
doc/src/sgml/storage.sgml
src/backend/access/gin/gininsert.c
src/backend/access/gist/gist.c
src/backend/access/hash/hash.c
src/backend/access/hash/hashovfl.c
src/backend/access/hash/hashpage.c
src/backend/access/nbtree/nbtree.c
src/backend/access/transam/xlog.c
src/backend/catalog/catalog.c
src/backend/catalog/heap.c
src/backend/catalog/index.c
src/backend/catalog/storage.c
src/backend/commands/tablecmds.c
src/backend/parser/gram.y
src/backend/storage/buffer/bufmgr.c
src/backend/storage/file/Makefile
src/backend/storage/file/copydir.c
src/backend/storage/file/fd.c
src/backend/storage/file/reinit.c [new file with mode: 0644]
src/backend/utils/adt/dbsize.c
src/backend/utils/cache/relcache.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/pg_dumpall.c
src/bin/psql/describe.c
src/include/access/gin.h
src/include/access/gist_private.h
src/include/access/hash.h
src/include/access/nbtree.h
src/include/catalog/catalog.h
src/include/catalog/catversion.h
src/include/catalog/pg_am.h
src/include/catalog/pg_class.h
src/include/catalog/pg_proc.h
src/include/catalog/storage.h
src/include/parser/kwlist.h
src/include/pg_config_manual.h
src/include/storage/buf_internals.h
src/include/storage/bufmgr.h
src/include/storage/copydir.h
src/include/storage/reinit.h [new file with mode: 0644]
src/include/storage/relfilenode.h
src/include/utils/rel.h

index 9fa20cfeee969def05a45515bbf0a9d0336c280e..0eeb499207e4b1bc1a6530dd957ce6b5560401fc 100644 (file)
       <entry><type>bool</type></entry>
       <entry></entry>
       <entry>
-       <literal>p</> = permanent table, <literal>t</> = temporary table
+       <literal>p</> = permanent table, <literal>u</> = unlogged table,
+       <literal>t</> = temporary table
       </entry>
      </row>
 
index c4eb59f7be7f0210afb22b4ce6bed4829d50c561..51e70e9200648c6bacb591308132ceb6fd63c4ee 100644 (file)
@@ -167,6 +167,17 @@ ambuild (Relation heapRelation,
 
   <para>
 <programlisting>
+void
+ambuildempty (Relation indexRelation);
+</programlisting>
+   Build an empty index, and write it to the initialization fork (INIT_FORKNUM)
+   of the given relation.  This method is called only for unlogged tables; the
+   empty index written to the initialization fork will be copied over the main
+   relation fork on each server restart.
+  </para>
+
+  <para>
+<programlisting>
 bool
 aminsert (Relation indexRelation,
           Datum *values,
index bc5dff0329621375cd55c10cd463f7227b4aab82..efb4b1aca18f68030ad7f52fcc2810f857a0398a 100644 (file)
@@ -21,7 +21,7 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [
+CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [
   { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
     | <replaceable>table_constraint</replaceable>
     | LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ] }
@@ -32,7 +32,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE [ IF NOT EXISTS ] <repl
 [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
 [ TABLESPACE <replaceable class="PARAMETER">tablespace</replaceable> ]
 
-CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable>
+CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable>
     OF <replaceable class="PARAMETER">type_name</replaceable> [ (
   { <replaceable class="PARAMETER">column_name</replaceable> WITH OPTIONS [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
     | <replaceable>table_constraint</replaceable> }
@@ -164,6 +164,23 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE [ IF NOT EXISTS ] <repl
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>UNLOGGED</></term>
+    <listitem>
+     <para>
+      If specified, the table is created as an unlogged table.  Data written
+      to unlogged tables is not written to the write-ahead log (see <xref
+      linkend="wal">), which makes them considerably faster than ordinary
+      tables.  However, they are not crash-safe: an unlogged table is
+      automatically truncated after a crash or unclean shutdown.  The contents
+      of an unlogged table are also not replicated to standby servers.
+      Any indexes created on an unlogged table are automatically unlogged as
+      well; however, unlogged <link linkend="GiST">GiST indexes</link> are
+      currently not supported and cannot be created on an unlogged table.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>IF NOT EXISTS</></term>
     <listitem>
index 3a256d1aaedb5eaf59b41a0253513b8e34db4491..ff71078d1e2ee797b50dac61a1a24dcd94a3d5d9 100644 (file)
@@ -21,7 +21,7 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable>table_name</replaceable>
+CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE <replaceable>table_name</replaceable>
     [ (<replaceable>column_name</replaceable> [, ...] ) ]
     [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
     [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
@@ -81,6 +81,16 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable>table_name
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>UNLOGGED</></term>
+    <listitem>
+     <para>
+      If specified, the table is created as an unlogged table.
+      Refer to <xref linkend="sql-createtable"> for details.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable>table_name</replaceable></term>
     <listitem>
index fd13c0d9e750af5c0a3ccc5623c85da6f5442342..b291a257ea70da71a864741137b681b54a1259e8 100644 (file)
@@ -669,6 +669,17 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--no-unlogged-table-data</option></term>
+      <listitem>
+       <para>
+        Do not dump the contents of unlogged tables.  This option has no
+        effect on whether or not the table definitions (schema) are dumped;
+        it only suppresses dumping the table data.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--quote-all-identifiers</></term>
       <listitem>
index 39da0b29490cf1fea71a12110911b25b3b40991a..04e95e876d1bac5832ca76775baf13b9118b979c 100644 (file)
@@ -201,6 +201,17 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--no-unlogged-table-data</option></term>
+      <listitem>
+       <para>
+        Do not dump the contents of unlogged tables.  This option has no
+        effect on whether or not the table definitions (schema) are dumped;
+        it only suppresses dumping the table data.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-r</option></term>
       <term><option>--roles-only</option></term>
index cda7f6452f5f6e24f4df7a2ec4065c48dbfb5c19..430df4a8438af55ee071cef9db1586237cb7c084 100644 (file)
@@ -147,7 +147,9 @@ the relation.  The free space map is stored in a file named with the filenode
 number plus the suffix <literal>_fsm</>.  Tables also have a
 <firstterm>visibility map</>, stored in a fork with the suffix <literal>_vm</>,
 to track which pages are known to have no dead tuples.  The visibility map is
-described further in <xref linkend="storage-vm">.
+described further in <xref linkend="storage-vm">.  Unlogged tables and indexes
+have a third fork, known as the initialization fork, which is stored in a fork
+with the suffix <literal>_init</literal> (see <xref linkend="storage-init">).
 </para>
 
 <caution>
@@ -485,6 +487,24 @@ a bit is not set, it might or might not be true.
 
 </sect1>
 
+<sect1 id="storage-init">
+
+<title>The Initialization Fork</title>
+
+<indexterm>
+ <primary>Initialization Fork</primary>
+</indexterm>
+
+<para>
+Each unlogged table, and each index on an unlogged table, has an initialization
+fork.  The initialization fork is an empty table or index of the appropriate
+type.  When an unlogged table must be reset to empty due to a crash, the
+initialization fork is copied over the main fork, and any other forks are
+erased (they will be recreated automatically as needed).
+</para>
+
+</sect1>
+
 <sect1 id="storage-page-layout">
 
 <title>Database Page Layout</title>
index 8681edefe673a29b287c844357e55412113c77ce..d66c79cb8de6b5cfa8d6b6417002a3044802b6c7 100644 (file)
@@ -19,6 +19,7 @@
 #include "catalog/index.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
+#include "storage/smgr.h"
 #include "storage/indexfsm.h"
 #include "utils/memutils.h"
 
@@ -411,6 +412,47 @@ ginbuild(PG_FUNCTION_ARGS)
        PG_RETURN_POINTER(result);
 }
 
+/*
+ *     ginbuildempty() -- build an empty gin index in the initialization fork
+ */
+Datum
+ginbuildempty(PG_FUNCTION_ARGS)
+{
+       Relation        index = (Relation) PG_GETARG_POINTER(0);
+       Buffer          RootBuffer,
+                               MetaBuffer;
+
+       /* An empty GIN index has two pages. */
+       MetaBuffer =
+               ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
+       LockBuffer(MetaBuffer, BUFFER_LOCK_EXCLUSIVE);
+       RootBuffer =
+               ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
+       LockBuffer(RootBuffer, BUFFER_LOCK_EXCLUSIVE);
+
+       /* Initialize both pages, mark them dirty, unlock and release buffer. */
+       START_CRIT_SECTION();
+       GinInitMetabuffer(MetaBuffer);
+       MarkBufferDirty(MetaBuffer);
+       GinInitBuffer(RootBuffer, GIN_LEAF);
+       MarkBufferDirty(RootBuffer);
+
+       /* XLOG the new pages */
+       log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+                               BufferGetBlockNumber(MetaBuffer),
+                               BufferGetPage(MetaBuffer));
+       log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+                               BufferGetBlockNumber(RootBuffer),
+                               BufferGetPage(RootBuffer));
+       END_CRIT_SECTION();
+
+       /* Unlock and release the buffers. */
+       UnlockReleaseBuffer(MetaBuffer);
+       UnlockReleaseBuffer(RootBuffer);
+
+       PG_RETURN_VOID();
+}
+
 /*
  * Inserts value during normal insertion
  */
index 7cd144e2f09c5aa3d2bf87cc1b5a7d63be7940ac..c26ac74332d59c32f83b54fcce016d3ea4dc655a 100644 (file)
@@ -218,6 +218,19 @@ gistbuildCallback(Relation index,
        MemoryContextReset(buildstate->tmpCtx);
 }
 
+/*
+ *     gistbuildempty() -- build an empty gist index in the initialization fork
+ */
+Datum
+gistbuildempty(PG_FUNCTION_ARGS)
+{
+       ereport(ERROR,
+                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                        errmsg("unlogged GIST indexes are not supported")));
+
+       PG_RETURN_VOID();
+}
+
 /*
  *     gistinsert -- wrapper for GiST tuple insertion.
  *
index e53ec3d5eaa2f6f9ba22a224855f60a996cffa15..4df92d44c03e6313b51fa9c0620897bc07ddb54d 100644 (file)
@@ -69,7 +69,7 @@ hashbuild(PG_FUNCTION_ARGS)
        estimate_rel_size(heap, NULL, &relpages, &reltuples);
 
        /* Initialize the hash index metadata page and initial buckets */
-       num_buckets = _hash_metapinit(index, reltuples);
+       num_buckets = _hash_metapinit(index, reltuples, MAIN_FORKNUM);
 
        /*
         * If we just insert the tuples into the index in scan order, then
@@ -113,6 +113,19 @@ hashbuild(PG_FUNCTION_ARGS)
        PG_RETURN_POINTER(result);
 }
 
+/*
+ *     hashbuildempty() -- build an empty hash index in the initialization fork
+ */
+Datum
+hashbuildempty(PG_FUNCTION_ARGS)
+{
+       Relation        index = (Relation) PG_GETARG_POINTER(0);
+
+       _hash_metapinit(index, 0, INIT_FORKNUM);
+
+       PG_RETURN_VOID();
+}
+
 /*
  * Per-tuple callback from IndexBuildHeapScan
  */
index 7c6e902ea93c38c4b1ccf053471844f9f17f7f37..454ad6c7a8addac501c0de55f84b7866ad78e361 100644 (file)
@@ -259,7 +259,7 @@ _hash_getovflpage(Relation rel, Buffer metabuf)
                 * convenient to pre-mark them as "in use" too.
                 */
                bit = metap->hashm_spares[splitnum];
-               _hash_initbitmap(rel, metap, bitno_to_blkno(metap, bit));
+               _hash_initbitmap(rel, metap, bitno_to_blkno(metap, bit), MAIN_FORKNUM);
                metap->hashm_spares[splitnum]++;
        }
        else
@@ -280,7 +280,7 @@ _hash_getovflpage(Relation rel, Buffer metabuf)
         * with metapage write lock held; would be better to use a lock that
         * doesn't block incoming searches.
         */
-       newbuf = _hash_getnewbuf(rel, blkno);
+       newbuf = _hash_getnewbuf(rel, blkno, MAIN_FORKNUM);
 
        metap->hashm_spares[splitnum]++;
 
@@ -503,7 +503,8 @@ _hash_freeovflpage(Relation rel, Buffer ovflbuf,
  * All bits in the new bitmap page are set to "1", indicating "in use".
  */
 void
-_hash_initbitmap(Relation rel, HashMetaPage metap, BlockNumber blkno)
+_hash_initbitmap(Relation rel, HashMetaPage metap, BlockNumber blkno,
+                                ForkNumber forkNum)
 {
        Buffer          buf;
        Page            pg;
@@ -520,7 +521,7 @@ _hash_initbitmap(Relation rel, HashMetaPage metap, BlockNumber blkno)
         * page while holding the metapage lock, but this path is taken so seldom
         * that it's not worth worrying about.
         */
-       buf = _hash_getnewbuf(rel, blkno);
+       buf = _hash_getnewbuf(rel, blkno, forkNum);
        pg = BufferGetPage(buf);
 
        /* initialize the page's special space */
index 2ebeda98b59f7eaee21e95f140351e135bb07ed2..29f7b25b4ec5f949ce28dee61291185a62edfffe 100644 (file)
@@ -183,9 +183,9 @@ _hash_getinitbuf(Relation rel, BlockNumber blkno)
  *             extend the index at a time.
  */
 Buffer
-_hash_getnewbuf(Relation rel, BlockNumber blkno)
+_hash_getnewbuf(Relation rel, BlockNumber blkno, ForkNumber forkNum)
 {
-       BlockNumber nblocks = RelationGetNumberOfBlocks(rel);
+       BlockNumber nblocks = RelationGetNumberOfBlocksInFork(rel, forkNum);
        Buffer          buf;
 
        if (blkno == P_NEW)
@@ -197,13 +197,13 @@ _hash_getnewbuf(Relation rel, BlockNumber blkno)
        /* smgr insists we use P_NEW to extend the relation */
        if (blkno == nblocks)
        {
-               buf = ReadBuffer(rel, P_NEW);
+               buf = ReadBufferExtended(rel, forkNum, P_NEW, RBM_NORMAL, NULL);
                if (BufferGetBlockNumber(buf) != blkno)
                        elog(ERROR, "unexpected hash relation size: %u, should be %u",
                                 BufferGetBlockNumber(buf), blkno);
        }
        else
-               buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO, NULL);
+               buf = ReadBufferExtended(rel, forkNum, blkno, RBM_ZERO, NULL);
 
        LockBuffer(buf, HASH_WRITE);
 
@@ -324,7 +324,7 @@ _hash_chgbufaccess(Relation rel,
  * multiple buffer locks is ignored.
  */
 uint32
-_hash_metapinit(Relation rel, double num_tuples)
+_hash_metapinit(Relation rel, double num_tuples, ForkNumber forkNum)
 {
        HashMetaPage metap;
        HashPageOpaque pageopaque;
@@ -340,7 +340,7 @@ _hash_metapinit(Relation rel, double num_tuples)
        uint32          i;
 
        /* safety check */
-       if (RelationGetNumberOfBlocks(rel) != 0)
+       if (RelationGetNumberOfBlocksInFork(rel, forkNum) != 0)
                elog(ERROR, "cannot initialize non-empty hash index \"%s\"",
                         RelationGetRelationName(rel));
 
@@ -383,7 +383,7 @@ _hash_metapinit(Relation rel, double num_tuples)
         * calls to occur.      This ensures that the smgr level has the right idea of
         * the physical index length.
         */
-       metabuf = _hash_getnewbuf(rel, HASH_METAPAGE);
+       metabuf = _hash_getnewbuf(rel, HASH_METAPAGE, forkNum);
        pg = BufferGetPage(metabuf);
 
        pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg);
@@ -451,7 +451,7 @@ _hash_metapinit(Relation rel, double num_tuples)
                /* Allow interrupts, in case N is huge */
                CHECK_FOR_INTERRUPTS();
 
-               buf = _hash_getnewbuf(rel, BUCKET_TO_BLKNO(metap, i));
+               buf = _hash_getnewbuf(rel, BUCKET_TO_BLKNO(metap, i), forkNum);
                pg = BufferGetPage(buf);
                pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg);
                pageopaque->hasho_prevblkno = InvalidBlockNumber;
@@ -468,7 +468,7 @@ _hash_metapinit(Relation rel, double num_tuples)
        /*
         * Initialize first bitmap page
         */
-       _hash_initbitmap(rel, metap, num_buckets + 1);
+       _hash_initbitmap(rel, metap, num_buckets + 1, forkNum);
 
        /* all done */
        _hash_wrtbuf(rel, metabuf);
@@ -785,7 +785,7 @@ _hash_splitbucket(Relation rel,
        oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);
 
        nblkno = start_nblkno;
-       nbuf = _hash_getnewbuf(rel, nblkno);
+       nbuf = _hash_getnewbuf(rel, nblkno, MAIN_FORKNUM);
        npage = BufferGetPage(nbuf);
 
        /* initialize the new bucket's primary page */
index 655a40090e98cb27bd82f9c1f9dee1aa72913c49..a13d629b0efda98b6c32c4b6417b7161a5a9bf8f 100644 (file)
@@ -29,6 +29,7 @@
 #include "storage/indexfsm.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
+#include "storage/smgr.h"
 #include "utils/memutils.h"
 
 
@@ -204,6 +205,36 @@ btbuildCallback(Relation index,
        pfree(itup);
 }
 
+/*
+ *     btbuildempty() -- build an empty btree index in the initialization fork
+ */
+Datum
+btbuildempty(PG_FUNCTION_ARGS)
+{
+       Relation        index = (Relation) PG_GETARG_POINTER(0);
+       Page            metapage;
+
+       /* Construct metapage. */
+       metapage = (Page) palloc(BLCKSZ);
+       _bt_initmetapage(metapage, P_NONE, 0);
+
+       /* Write the page.  If archiving/streaming, XLOG it. */
+       smgrwrite(index->rd_smgr, INIT_FORKNUM, BTREE_METAPAGE,
+                         (char *) metapage, true);
+       if (XLogIsNeeded())
+               log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
+                                       BTREE_METAPAGE, metapage);
+
+       /*
+        * An immediate sync is require even if we xlog'd the page, because the
+        * write did not go through shared_buffers and therefore a concurrent
+        * checkpoint may have move the redo pointer past our xlog record.
+        */
+       smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
+
+       PG_RETURN_VOID();
+}
+
 /*
  *     btinsert() -- insert an index tuple into a btree.
  *
index bf62138bf860b02c87b2247c3dc2baeac0a71ada..1ec6f2f15ac7da925176a59d1a7e87045f3dde7d 100644 (file)
@@ -49,6 +49,7 @@
 #include "storage/latch.h"
 #include "storage/pmsignal.h"
 #include "storage/procarray.h"
+#include "storage/reinit.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/builtins.h"
@@ -5960,6 +5961,14 @@ StartupXLOG(void)
                /* Check that the GUCs used to generate the WAL allow recovery */
                CheckRequiredParameterValues();
 
+               /*
+                * We're in recovery, so unlogged relations relations may be trashed
+                * and must be reset.  This should be done BEFORE allowing Hot
+                * Standby connections, so that read-only backends don't try to
+                * read whatever garbage is left over from before.
+                */
+               ResetUnloggedRelations(UNLOGGED_RELATION_CLEANUP);
+
                /*
                 * Initialize for Hot Standby, if enabled. We won't let backends in
                 * yet, not until we've reached the min recovery point specified in
@@ -6413,6 +6422,14 @@ StartupXLOG(void)
         */
        PreallocXlogFiles(EndOfLog);
 
+       /*
+        * Reset initial contents of unlogged relations.  This has to be done
+        * AFTER recovery is complete so that any unlogged relations created
+        * during recovery also get picked up.
+        */
+       if (InRecovery)
+               ResetUnloggedRelations(UNLOGGED_RELATION_INIT);
+
        /*
         * Okay, we're officially UP.
         */
index 88b5c2a215d10827df0406cb53cd18e9b199332f..fc5a8fcd655cd4c015717e18c5e705fb4655ea1f 100644 (file)
@@ -55,7 +55,8 @@
 const char *forkNames[] = {
        "main",                                         /* MAIN_FORKNUM */
        "fsm",                                          /* FSM_FORKNUM */
-       "vm"                                            /* VISIBILITYMAP_FORKNUM */
+       "vm",                                           /* VISIBILITYMAP_FORKNUM */
+       "init"                                          /* INIT_FORKNUM */
 };
 
 /*
@@ -82,14 +83,14 @@ forkname_to_number(char *forkName)
  *             We use this to figure out whether a filename could be a relation
  *             fork (as opposed to an oddly named stray file that somehow ended
  *             up in the database directory).  If the passed string begins with
- *             a fork name (other than the main fork name), we return its length.
- *             If not, we return 0.
+ *             a fork name (other than the main fork name), we return its length,
+ *         and set *fork (if not NULL) to the fork number.  If not, we return 0.
  *
  * Note that the present coding assumes that there are no fork names which
  * are prefixes of other fork names.
  */
 int
-forkname_chars(const char *str)
+forkname_chars(const char *str, ForkNumber *fork)
 {
        ForkNumber      forkNum;
 
@@ -97,7 +98,11 @@ forkname_chars(const char *str)
        {
                int len = strlen(forkNames[forkNum]);
                if (strncmp(forkNames[forkNum], str, len) == 0)
+               {
+                       if (fork)
+                               *fork = forkNum;
                        return len;
+               }
        }
        return 0;
 }
@@ -537,6 +542,7 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
                case RELPERSISTENCE_TEMP:
                        backend = MyBackendId;
                        break;
+               case RELPERSISTENCE_UNLOGGED:
                case RELPERSISTENCE_PERMANENT:
                        backend = InvalidBackendId;
                        break;
index bcf6caa2eef325c82c8d5f4160ecd37908fc3e3b..8027d740f6d35a67389aa5d9453a8dfeecde390b 100644 (file)
@@ -1210,6 +1210,25 @@ heap_create_with_catalog(const char *relname,
        if (oncommit != ONCOMMIT_NOOP)
                register_on_commit_action(relid, oncommit);
 
+       /*
+        * If this is an unlogged relation, it needs an init fork so that it
+        * can be correctly reinitialized on restart.  Since we're going to
+        * do an immediate sync, we ony need to xlog this if archiving or
+        * streaming is enabled.  And the immediate sync is required, because
+        * otherwise there's no guarantee that this will hit the disk before
+        * the next checkpoint moves the redo pointer.
+        */
+       if (relpersistence == RELPERSISTENCE_UNLOGGED)
+       {
+               Assert(relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE);
+
+               smgrcreate(new_rel_desc->rd_smgr, INIT_FORKNUM, false);
+               if (XLogIsNeeded())
+                       log_smgrcreate(&new_rel_desc->rd_smgr->smgr_rnode.node,
+                                                  INIT_FORKNUM);
+               smgrimmedsync(new_rel_desc->rd_smgr, INIT_FORKNUM);
+       }
+
        /*
         * ok, the relation has been cataloged, so close our relations and return
         * the OID of the newly created relation.
index 8fbe8ebc91d4e27a8c07753701263e41bd6284c8..e50a084f003c7e44de472b349307fb6d9f171a88 100644 (file)
@@ -1437,6 +1437,17 @@ index_build(Relation heapRelation,
                                                                                 PointerGetDatum(indexInfo)));
        Assert(PointerIsValid(stats));
 
+       /*
+        * If this is an unlogged index, we need to write out an init fork for it.
+        */
+       if (heapRelation->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
+       {
+               RegProcedure    ambuildempty = indexRelation->rd_am->ambuildempty;
+               RelationOpenSmgr(indexRelation);
+               smgrcreate(indexRelation->rd_smgr, INIT_FORKNUM, false);
+               OidFunctionCall1(ambuildempty, PointerGetDatum(indexRelation));
+       }
+
        /*
         * If it's for an exclusion constraint, make a second pass over the heap
         * to verify that the constraint is satisfied.
index 671aaff133a25a3969604081d0b1570de4d02988..0bd0451f0085a708374fc9373032834c3ad2b869 100644 (file)
@@ -74,6 +74,7 @@ static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */
 typedef struct xl_smgr_create
 {
        RelFileNode rnode;
+       ForkNumber      forkNum;
 } xl_smgr_create;
 
 typedef struct xl_smgr_truncate
@@ -98,9 +99,6 @@ void
 RelationCreateStorage(RelFileNode rnode, char relpersistence)
 {
        PendingRelDelete *pending;
-       XLogRecPtr      lsn;
-       XLogRecData rdata;
-       xl_smgr_create xlrec;
        SMgrRelation srel;
        BackendId       backend;
        bool            needs_wal;
@@ -111,6 +109,10 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
                        backend = MyBackendId;
                        needs_wal = false;
                        break;
+               case RELPERSISTENCE_UNLOGGED:
+                       backend = InvalidBackendId;
+                       needs_wal = false;
+                       break;
                case RELPERSISTENCE_PERMANENT:
                        backend = InvalidBackendId;
                        needs_wal = true;
@@ -124,19 +126,7 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
        smgrcreate(srel, MAIN_FORKNUM, false);
 
        if (needs_wal)
-       {
-               /*
-                * Make an XLOG entry reporting the file creation.
-                */
-               xlrec.rnode = rnode;
-
-               rdata.data = (char *) &xlrec;
-               rdata.len = sizeof(xlrec);
-               rdata.buffer = InvalidBuffer;
-               rdata.next = NULL;
-
-               lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE, &rdata);
-       }
+               log_smgrcreate(&srel->smgr_rnode.node, MAIN_FORKNUM);
 
        /* Add the relation to the list of stuff to delete at abort */
        pending = (PendingRelDelete *)
@@ -149,6 +139,29 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
        pendingDeletes = pending;
 }
 
+/*
+ * Perform XLogInsert of a XLOG_SMGR_CREATE record to WAL.
+ */
+void
+log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum)
+{
+       xl_smgr_create xlrec;
+       XLogRecData rdata;
+
+       /*
+        * Make an XLOG entry reporting the file creation.
+        */
+       xlrec.rnode = *rnode;
+       xlrec.forkNum = forkNum;
+
+       rdata.data = (char *) &xlrec;
+       rdata.len = sizeof(xlrec);
+       rdata.buffer = InvalidBuffer;
+       rdata.next = NULL;
+
+       XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE, &rdata);
+}
+
 /*
  * RelationDropStorage
  *             Schedule unlinking of physical storage at transaction commit.
@@ -478,7 +491,7 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record)
                SMgrRelation reln;
 
                reln = smgropen(xlrec->rnode, InvalidBackendId);
-               smgrcreate(reln, MAIN_FORKNUM, true);
+               smgrcreate(reln, xlrec->forkNum, true);
        }
        else if (info == XLOG_SMGR_TRUNCATE)
        {
@@ -523,7 +536,7 @@ smgr_desc(StringInfo buf, uint8 xl_info, char *rec)
        if (info == XLOG_SMGR_CREATE)
        {
                xl_smgr_create *xlrec = (xl_smgr_create *) rec;
-               char       *path = relpathperm(xlrec->rnode, MAIN_FORKNUM);
+               char       *path = relpathperm(xlrec->rnode, xlrec->forkNum);
 
                appendStringInfo(buf, "file create: %s", path);
                pfree(path);
index 6729d8336f570e59fd514fef6bab75dd3bb16ff3..3f6b814f02cb708dc9979a254eff796f82962ce3 100644 (file)
@@ -5128,12 +5128,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
                                                RelationGetRelationName(pkrel))));
 
        /*
-        * References from permanent tables to temp tables are disallowed because
-        * the contents of the temp table disappear at the end of each session.
-        * References from temp tables to permanent tables are also disallowed,
-        * because other backends might need to run the RI triggers on the perm
-        * table, but they can't reliably see tuples in the local buffers of other
-        * backends.
+        * References from permanent or unlogged tables to temp tables, and from
+        * permanent tables to unlogged tables, are disallowed because the
+        * referenced data can vanish out from under us.  References from temp
+        * tables to any other table type are also disallowed, because other
+        * backends might need to run the RI triggers on the perm table, but they
+        * can't reliably see tuples in the local buffers of other backends.
         */
        switch (rel->rd_rel->relpersistence)
        {
@@ -5143,6 +5143,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
                                                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                                 errmsg("constraints on permanent tables may reference only permanent tables")));
                        break;
+               case RELPERSISTENCE_UNLOGGED:
+                       if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT
+                               && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                                errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
+                       break;
                case RELPERSISTENCE_TEMP:
                        if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
                                ereport(ERROR,
index 37840baa0f690fa5c386da596d5a40c5cacb31f4..26a5e84d44a20bd4975b6750130a09e140a8055a 100644 (file)
@@ -538,8 +538,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
        TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
        TRUNCATE TRUSTED TYPE_P
 
-       UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
-       UPDATE USER USING
+       UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
+       UNTIL UPDATE USER USING
 
        VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
        VERBOSE VERSION_P VIEW VOLATILE
@@ -2365,6 +2365,7 @@ OptTemp:  TEMPORARY                                       { $$ = RELPERSISTENCE_TEMP; }
                        | LOCAL TEMP                            { $$ = RELPERSISTENCE_TEMP; }
                        | GLOBAL TEMPORARY                      { $$ = RELPERSISTENCE_TEMP; }
                        | GLOBAL TEMP                           { $$ = RELPERSISTENCE_TEMP; }
+                       | UNLOGGED                                      { $$ = RELPERSISTENCE_UNLOGGED; }
                        | /*EMPTY*/                                     { $$ = RELPERSISTENCE_PERMANENT; }
                ;
 
@@ -7927,6 +7928,11 @@ OptTempTableName:
                                        $$ = $4;
                                        $$->relpersistence = RELPERSISTENCE_TEMP;
                                }
+                       | UNLOGGED opt_table qualified_name
+                               {
+                                       $$ = $3;
+                                       $$->relpersistence = RELPERSISTENCE_UNLOGGED;
+                               }
                        | TABLE qualified_name
                                {
                                        $$ = $2;
@@ -11395,6 +11401,7 @@ unreserved_keyword:
                        | UNENCRYPTED
                        | UNKNOWN
                        | UNLISTEN
+                       | UNLOGGED
                        | UNTIL
                        | UPDATE
                        | VACUUM
index 860e736ff05b5b8a05844cc83df8e48fb16950e0..34e54536692b55e877aa2e5cf16b9556f855f4b0 100644 (file)
@@ -82,7 +82,7 @@ static bool IsForInput;
 static volatile BufferDesc *PinCountWaitBuf = NULL;
 
 
-static Buffer ReadBuffer_common(SMgrRelation reln,
+static Buffer ReadBuffer_common(SMgrRelation reln, char relpersistence,
                                  ForkNumber forkNum, BlockNumber blockNum,
                                  ReadBufferMode mode, BufferAccessStrategy strategy,
                                  bool *hit);
@@ -97,7 +97,9 @@ static void TerminateBufferIO(volatile BufferDesc *buf, bool clear_dirty,
                                  int set_flag_bits);
 static void shared_buffer_write_error_callback(void *arg);
 static void local_buffer_write_error_callback(void *arg);
-static volatile BufferDesc *BufferAlloc(SMgrRelation smgr, ForkNumber forkNum,
+static volatile BufferDesc *BufferAlloc(SMgrRelation smgr,
+                       char relpersistence,
+                       ForkNumber forkNum,
                        BlockNumber blockNum,
                        BufferAccessStrategy strategy,
                        bool *foundPtr);
@@ -241,8 +243,8 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
         * miss.
         */
        pgstat_count_buffer_read(reln);
-       buf = ReadBuffer_common(reln->rd_smgr, forkNum, blockNum,
-                                                       mode, strategy, &hit);
+       buf = ReadBuffer_common(reln->rd_smgr, reln->rd_rel->relpersistence,
+                                                       forkNum, blockNum, mode, strategy, &hit);
        if (hit)
                pgstat_count_buffer_hit(reln);
        return buf;
@@ -253,10 +255,10 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
  * ReadBufferWithoutRelcache -- like ReadBufferExtended, but doesn't require
  *             a relcache entry for the relation.
  *
- * NB: At present, this function may not be used on temporary relations, which
+ * NB: At present, this function may only be used on permanent relations, which
  * is OK, because we only use it during XLOG replay.  If in the future we
- * want to use it on temporary relations, we could pass the backend ID as an
- * additional parameter.
+ * want to use it on temporary or unlogged relations, we could pass additional
+ * parameters.
  */
 Buffer
 ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
@@ -267,7 +269,8 @@ ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
 
        SMgrRelation smgr = smgropen(rnode, InvalidBackendId);
 
-       return ReadBuffer_common(smgr, forkNum, blockNum, mode, strategy, &hit);
+       return ReadBuffer_common(smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum,
+                                                        mode, strategy, &hit);
 }
 
 
@@ -277,7 +280,7 @@ ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
  * *hit is set to true if the request was satisfied from shared buffer cache.
  */
 static Buffer
-ReadBuffer_common(SMgrRelation smgr, ForkNumber forkNum,
+ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
                                  BlockNumber blockNum, ReadBufferMode mode,
                                  BufferAccessStrategy strategy, bool *hit)
 {
@@ -319,7 +322,8 @@ ReadBuffer_common(SMgrRelation smgr, ForkNumber forkNum,
                 * lookup the buffer.  IO_IN_PROGRESS is set if the requested block is
                 * not currently in memory.
                 */
-               bufHdr = BufferAlloc(smgr, forkNum, blockNum, strategy, &found);
+               bufHdr = BufferAlloc(smgr, relpersistence, forkNum, blockNum,
+                                                        strategy, &found);
                if (found)
                        pgBufferUsage.shared_blks_hit++;
                else
@@ -500,7 +504,7 @@ ReadBuffer_common(SMgrRelation smgr, ForkNumber forkNum,
  * No locks are held either at entry or exit.
  */
 static volatile BufferDesc *
-BufferAlloc(SMgrRelation smgr, ForkNumber forkNum,
+BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
                        BlockNumber blockNum,
                        BufferAccessStrategy strategy,
                        bool *foundPtr)
@@ -797,8 +801,11 @@ BufferAlloc(SMgrRelation smgr, ForkNumber forkNum,
         * 1 so that the buffer can survive one clock-sweep pass.)
         */
        buf->tag = newTag;
-       buf->flags &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_CHECKPOINT_NEEDED | BM_IO_ERROR);
-       buf->flags |= BM_TAG_VALID;
+       buf->flags &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_CHECKPOINT_NEEDED | BM_IO_ERROR | BM_PERMANENT);
+       if (relpersistence == RELPERSISTENCE_PERMANENT)
+               buf->flags |= BM_TAG_VALID | BM_PERMANENT;
+       else
+               buf->flags |= BM_TAG_VALID;
        buf->usage_count = 1;
 
        UnlockBufHdr(buf);
@@ -1155,8 +1162,10 @@ UnpinBuffer(volatile BufferDesc *buf, bool fixOwner)
  * BufferSync -- Write out all dirty buffers in the pool.
  *
  * This is called at checkpoint time to write out all dirty shared buffers.
- * The checkpoint request flags should be passed in; currently the only one
- * examined is CHECKPOINT_IMMEDIATE, which disables delays between writes.
+ * The checkpoint request flags should be passed in.  If CHECKPOINT_IMMEDIATE
+ * is set, we disable delays between writes; if CHECKPOINT_IS_SHUTDOWN is
+ * set, we write even unlogged buffers, which are otherwise skipped.  The
+ * remaining flags currently have no effect here.
  */
 static void
 BufferSync(int flags)
@@ -1165,10 +1174,18 @@ BufferSync(int flags)
        int                     num_to_scan;
        int                     num_to_write;
        int                     num_written;
+       int                     mask = BM_DIRTY;
 
        /* Make sure we can handle the pin inside SyncOneBuffer */
        ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
 
+       /*
+        * Unless this is a shutdown checkpoint, we write only permanent, dirty
+        * buffers.  But at shutdown time, we write all dirty buffers.
+        */
+       if (!(flags & CHECKPOINT_IS_SHUTDOWN))
+               flags |= BM_PERMANENT;
+
        /*
         * Loop over all buffers, and mark the ones that need to be written with
         * BM_CHECKPOINT_NEEDED.  Count them as we go (num_to_write), so that we
@@ -1196,7 +1213,7 @@ BufferSync(int flags)
                 */
                LockBufHdr(bufHdr);
 
-               if (bufHdr->flags & BM_DIRTY)
+               if ((bufHdr->flags & mask) == mask)
                {
                        bufHdr->flags |= BM_CHECKPOINT_NEEDED;
                        num_to_write++;
@@ -1897,12 +1914,12 @@ FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln)
  *             Determines the current number of pages in the relation.
  */
 BlockNumber
-RelationGetNumberOfBlocks(Relation relation)
+RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
 {
        /* Open it at the smgr level if not already done */
        RelationOpenSmgr(relation);
 
-       return smgrnblocks(relation->rd_smgr, MAIN_FORKNUM);
+       return smgrnblocks(relation->rd_smgr, forkNum);
 }
 
 /* ---------------------------------------------------------------------
index 3b93aa1b45d3dcc724f1f48a3cc5228de196ab00..d2198f2b93e8fda8cbd015e98d1ccce6b1412105 100644 (file)
@@ -12,6 +12,6 @@ subdir = src/backend/storage/file
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = fd.o buffile.o copydir.o
+OBJS = fd.o buffile.o copydir.o reinit.o
 
 include $(top_srcdir)/src/backend/common.mk
index f7dc509b500a47e3e275efec55c8bd97a173f4de..587fb9260c0f64540c585d5a044bd86f599a7bd0 100644 (file)
@@ -38,7 +38,6 @@
 #endif
 
 
-static void copy_file(char *fromfile, char *tofile);
 static void fsync_fname(char *fname, bool isdir);
 
 
@@ -142,7 +141,7 @@ copydir(char *fromdir, char *todir, bool recurse)
 /*
  * copy one file
  */
-static void
+void
 copy_file(char *fromfile, char *tofile)
 {
        char       *buffer;
index 4f7dc39d6388c0cb1fd5265b1b77c8e8120f9b6a..a1dc18be44b623cf72c65fb93a7ead84beac1d71 100644 (file)
@@ -2055,7 +2055,7 @@ looks_like_temp_rel_name(const char *name)
        /* We might have _forkname or .segment or both. */
        if (name[pos] == '_')
        {
-               int             forkchar = forkname_chars(&name[pos+1]);
+               int             forkchar = forkname_chars(&name[pos+1], NULL);
                if (forkchar <= 0)
                        return false;
                pos += forkchar + 1;
diff --git a/src/backend/storage/file/reinit.c b/src/backend/storage/file/reinit.c
new file mode 100644 (file)
index 0000000..b75178b
--- /dev/null
@@ -0,0 +1,396 @@
+/*-------------------------------------------------------------------------
+ *
+ * reinit.c
+ *       Reinitialization of unlogged relations
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *       src/backend/storage/file/reinit.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <unistd.h>
+
+#include "catalog/catalog.h"
+#include "storage/copydir.h"
+#include "storage/fd.h"
+#include "storage/reinit.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+
+static void ResetUnloggedRelationsInTablespaceDir(const char *tsdirname,
+                                                                        int op);
+static void ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname,
+                                                                  int op);
+static bool parse_filename_for_nontemp_relation(const char *name,
+                                                                       int *oidchars, ForkNumber *fork);
+
+typedef struct {
+       char oid[OIDCHARS+1];
+} unlogged_relation_entry;
+
+/*
+ * Reset unlogged relations from before the last restart.
+ *
+ * If op includes UNLOGGED_RELATION_CLEANUP, we remove all forks of any
+ * relation with an "init" fork, except for the "init" fork itself.
+ *
+ * If op includes UNLOGGED_RELATION_INIT, we copy the "init" fork to the main
+ * fork.
+ */
+void
+ResetUnloggedRelations(int op)
+{
+       char            temp_path[MAXPGPATH];
+       DIR                *spc_dir;
+       struct dirent *spc_de;
+       MemoryContext tmpctx, oldctx;
+
+       /* Log it. */
+       ereport(DEBUG1,
+                       (errmsg("resetting unlogged relations: cleanup %d init %d",
+                        (op & UNLOGGED_RELATION_CLEANUP) != 0,
+                        (op & UNLOGGED_RELATION_INIT) != 0)));
+
+       /*
+        * Just to be sure we don't leak any memory, let's create a temporary
+        * memory context for this operation.
+        */
+       tmpctx = AllocSetContextCreate(CurrentMemoryContext,
+                                                                  "ResetUnloggedRelations",
+                                                                  ALLOCSET_DEFAULT_MINSIZE,
+                                                                  ALLOCSET_DEFAULT_INITSIZE,
+                                                                  ALLOCSET_DEFAULT_MAXSIZE);
+       oldctx = MemoryContextSwitchTo(tmpctx);
+
+       /*
+        * First process unlogged files in pg_default ($PGDATA/base)
+        */
+       ResetUnloggedRelationsInTablespaceDir("base", op);
+
+       /*
+        * Cycle through directories for all non-default tablespaces.
+        */
+       spc_dir = AllocateDir("pg_tblspc");
+
+       while ((spc_de = ReadDir(spc_dir, "pg_tblspc")) != NULL)
+       {
+               if (strcmp(spc_de->d_name, ".") == 0 ||
+                       strcmp(spc_de->d_name, "..") == 0)
+                       continue;
+
+               snprintf(temp_path, sizeof(temp_path), "pg_tblspc/%s/%s",
+                       spc_de->d_name, TABLESPACE_VERSION_DIRECTORY);
+               ResetUnloggedRelationsInTablespaceDir(temp_path, op);
+       }
+
+       FreeDir(spc_dir);
+
+       /*
+        * Restore memory context.
+        */
+       MemoryContextSwitchTo(oldctx);
+       MemoryContextDelete(tmpctx);
+}
+
+/* Process one tablespace directory for ResetUnloggedRelations */
+static void
+ResetUnloggedRelationsInTablespaceDir(const char *tsdirname, int op)
+{
+       DIR                *ts_dir;
+       struct dirent *de;
+       char            dbspace_path[MAXPGPATH];
+
+       ts_dir = AllocateDir(tsdirname);
+       if (ts_dir == NULL)
+       {
+               /* anything except ENOENT is fishy */
+               if (errno != ENOENT)
+                       elog(LOG,
+                                "could not open tablespace directory \"%s\": %m",
+                                tsdirname);
+               return;
+       }
+
+       while ((de = ReadDir(ts_dir, tsdirname)) != NULL)
+       {
+               int             i = 0;
+
+               /*
+                * We're only interested in the per-database directories, which have
+                * numeric names.  Note that this code will also (properly) ignore "."
+                * and "..".
+                */
+               while (isdigit((unsigned char) de->d_name[i]))
+                       ++i;
+               if (de->d_name[i] != '\0' || i == 0)
+                       continue;
+
+               snprintf(dbspace_path, sizeof(dbspace_path), "%s/%s",
+                                tsdirname, de->d_name);
+               ResetUnloggedRelationsInDbspaceDir(dbspace_path, op);
+       }
+
+       FreeDir(ts_dir);
+}
+
+/* Process one per-dbspace directory for ResetUnloggedRelations */
+static void
+ResetUnloggedRelationsInDbspaceDir(const char *dbspacedirname, int op)
+{
+       DIR                *dbspace_dir;
+       struct dirent *de;
+       char            rm_path[MAXPGPATH];
+
+       /* Caller must specify at least one operation. */
+       Assert((op & (UNLOGGED_RELATION_CLEANUP | UNLOGGED_RELATION_INIT)) != 0);
+
+       /*
+        * Cleanup is a two-pass operation.  First, we go through and identify all
+        * the files with init forks.  Then, we go through again and nuke
+        * everything with the same OID except the init fork.
+        */
+       if ((op & UNLOGGED_RELATION_CLEANUP) != 0)
+       {
+               HTAB       *hash = NULL;
+               HASHCTL         ctl;
+
+               /* Open the directory. */
+               dbspace_dir = AllocateDir(dbspacedirname);
+               if (dbspace_dir == NULL)
+               {
+                       elog(LOG,
+                                "could not open dbspace directory \"%s\": %m",
+                                dbspacedirname);
+                       return;
+               }
+
+               /*
+                * It's possible that someone could create a ton of unlogged relations
+                * in the same database & tablespace, so we'd better use a hash table
+                * rather than an array or linked list to keep track of which files
+                * need to be reset.  Otherwise, this cleanup operation would be
+                * O(n^2).
+                */
+               ctl.keysize = sizeof(unlogged_relation_entry);
+               ctl.entrysize = sizeof(unlogged_relation_entry);
+               hash = hash_create("unlogged hash", 32, &ctl, HASH_ELEM);
+
+               /* Scan the directory. */
+               while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
+               {
+                       ForkNumber forkNum;
+                       int             oidchars;
+                       unlogged_relation_entry ent;
+
+                       /* Skip anything that doesn't look like a relation data file. */
+                       if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars,
+                                                                                                        &forkNum))
+                               continue;
+
+                       /* Also skip it unless this is the init fork. */
+                       if (forkNum != INIT_FORKNUM)
+                               continue;
+
+                       /*
+                        * Put the OID portion of the name into the hash table, if it isn't
+                        * already.
+                        */
+                       memset(ent.oid, 0, sizeof(ent.oid));
+                       memcpy(ent.oid, de->d_name, oidchars);
+                       hash_search(hash, &ent, HASH_ENTER, NULL);
+               }
+
+               /* Done with the first pass. */
+               FreeDir(dbspace_dir);
+
+               /*
+                * If we didn't find any init forks, there's no point in continuing;
+                * we can bail out now.
+                */
+               if (hash_get_num_entries(hash) == 0)
+               {
+                       hash_destroy(hash);
+                       return;
+               }
+
+               /*
+                * Now, make a second pass and remove anything that matches. First,
+                * reopen the directory.
+                */
+               dbspace_dir = AllocateDir(dbspacedirname);
+               if (dbspace_dir == NULL)
+               {
+                       elog(LOG,
+                                "could not open dbspace directory \"%s\": %m",
+                                dbspacedirname);
+                       hash_destroy(hash);
+                       return;
+               }
+
+               /* Scan the directory. */
+               while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
+               {
+                       ForkNumber forkNum;
+                       int             oidchars;
+                       bool    found;
+                       unlogged_relation_entry ent;
+
+                       /* Skip anything that doesn't look like a relation data file. */
+                       if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars,
+                                                                                                        &forkNum))
+                               continue;
+
+                       /* We never remove the init fork. */
+                       if (forkNum == INIT_FORKNUM)
+                               continue;
+
+                       /*
+                        * See whether the OID portion of the name shows up in the hash
+                        * table.
+                        */
+                       memset(ent.oid, 0, sizeof(ent.oid));
+                       memcpy(ent.oid, de->d_name, oidchars);
+                       hash_search(hash, &ent, HASH_FIND, &found);
+
+                       /* If so, nuke it! */
+                       if (found)
+                       {
+                               snprintf(rm_path, sizeof(rm_path), "%s/%s",
+                                       dbspacedirname, de->d_name);
+                               /*
+                                * It's tempting to actually throw an error here, but since
+                                * this code gets run during database startup, that could
+                                * result in the database failing to start.  (XXX Should we do
+                                * it anyway?)
+                                */
+                               if (unlink(rm_path))
+                                       elog(LOG, "could not unlink file \"%s\": %m", rm_path);
+                               else
+                                       elog(DEBUG2, "unlinked file \"%s\"", rm_path);
+                       }
+               }
+
+               /* Cleanup is complete. */
+               FreeDir(dbspace_dir);
+               hash_destroy(hash);
+       }
+
+       /*
+        * Initialization happens after cleanup is complete: we copy each init
+        * fork file to the corresponding main fork file.  Note that if we are
+        * asked to do both cleanup and init, we may never get here: if the cleanup
+        * code determines that there are no init forks in this dbspace, it will
+        * return before we get to this point.
+        */
+       if ((op & UNLOGGED_RELATION_INIT) != 0)
+       {
+               /* Open the directory. */
+               dbspace_dir = AllocateDir(dbspacedirname);
+               if (dbspace_dir == NULL)
+               {
+                       /* we just saw this directory, so it really ought to be there */
+                       elog(LOG,
+                                "could not open dbspace directory \"%s\": %m",
+                                dbspacedirname);
+                       return;
+               }
+
+               /* Scan the directory. */
+               while ((de = ReadDir(dbspace_dir, dbspacedirname)) != NULL)
+               {
+                       ForkNumber forkNum;
+                       int             oidchars;
+                       char    oidbuf[OIDCHARS+1];
+                       char    srcpath[MAXPGPATH];
+                       char    dstpath[MAXPGPATH];
+
+                       /* Skip anything that doesn't look like a relation data file. */
+                       if (!parse_filename_for_nontemp_relation(de->d_name, &oidchars,
+                                                                                                        &forkNum))
+                               continue;
+
+                       /* Also skip it unless this is the init fork. */
+                       if (forkNum != INIT_FORKNUM)
+                               continue;
+
+                       /* Construct source pathname. */
+                       snprintf(srcpath, sizeof(srcpath), "%s/%s",
+                                        dbspacedirname, de->d_name);
+
+                       /* Construct destination pathname. */
+                       memcpy(oidbuf, de->d_name, oidchars);
+                       oidbuf[oidchars] = '\0';
+                       snprintf(dstpath, sizeof(dstpath), "%s/%s%s",
+                                        dbspacedirname, oidbuf, de->d_name + oidchars + 1 +
+                                        strlen(forkNames[INIT_FORKNUM]));
+
+                       /* OK, we're ready to perform the actual copy. */
+                       elog(DEBUG2, "copying %s to %s", srcpath, dstpath);
+                       copy_file(srcpath, dstpath);
+               }
+
+               /* Done with the first pass. */
+               FreeDir(dbspace_dir);
+       }
+}
+
+/*
+ * Basic parsing of putative relation filenames.
+ *
+ * This funtion returns true if the file appears to be in the correct format
+ * for a non-temporary relation and false otherwise.
+ *
+ * NB: If this function returns true, the caller is entitled to assume that
+ * *oidchars has been set to the a value no more than OIDCHARS, and thus
+ * that a buffer of OIDCHARS+1 characters is sufficient to hold the OID
+ * portion of the filename.  This is critical to protect against a possible
+ * buffer overrun.
+ */
+static bool
+parse_filename_for_nontemp_relation(const char *name, int *oidchars,
+                                                                       ForkNumber *fork)
+{
+       int                     pos;
+
+       /* Look for a non-empty string of digits (that isn't too long). */
+       for (pos = 0; isdigit((unsigned char) name[pos]); ++pos)
+               ;
+       if (pos == 0 || pos > OIDCHARS)
+               return false;
+       *oidchars = pos;
+
+       /* Check for a fork name. */
+       if (name[pos] != '_')
+               *fork = MAIN_FORKNUM;
+       else
+       {
+               int             forkchar;
+
+               forkchar = forkname_chars(&name[pos+1], fork);
+               if (forkchar <= 0)
+                       return false;
+               pos += forkchar + 1;
+       }
+
+       /* Check for a segment number. */
+       if (name[pos] == '.')
+       {
+               int             segchar;
+               for (segchar = 1; isdigit((unsigned char) name[pos+segchar]); ++segchar)
+                       ;
+               if (segchar <= 1)
+                       return false;
+               pos += segchar;
+       }
+
+       /* Now we should be at the end. */
+       if (name[pos] != '\0')
+               return false;
+       return true;
+}
index e352cdafb3bbafbc4f47e95ae66dbb8997e4eb3c..f33c29e4b21cc538191a33bb5269365a4bfaefca 100644 (file)
@@ -615,6 +615,7 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
        /* Determine owning backend. */
        switch (relform->relpersistence)
        {
+               case RELPERSISTENCE_UNLOGGED:
                case RELPERSISTENCE_PERMANENT:
                        backend = InvalidBackendId;
                        break;
index 1509686079b0f88f85f72a7a14127be07d711907..fa9e9ca3a4e389548cc10ef18440a14483b0da65 100644 (file)
@@ -851,6 +851,7 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
        relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
        switch (relation->rd_rel->relpersistence)
        {
+               case RELPERSISTENCE_UNLOGGED:
                case RELPERSISTENCE_PERMANENT:
                        relation->rd_backend = InvalidBackendId;
                        break;
@@ -2490,6 +2491,7 @@ RelationBuildLocalRelation(const char *relname,
        rel->rd_rel->relpersistence = relpersistence;
        switch (relpersistence)
        {
+               case RELPERSISTENCE_UNLOGGED:
                case RELPERSISTENCE_PERMANENT:
                        rel->rd_backend = InvalidBackendId;
                        break;
index 66274b442e6eb7aedab60bf23c03795c02d9b182..afd759142bb4010aec67df46a335a6ab15f908eb 100644 (file)
@@ -134,6 +134,7 @@ static int  disable_dollar_quoting = 0;
 static int     dump_inserts = 0;
 static int     column_inserts = 0;
 static int     no_security_label = 0;
+static int     no_unlogged_table_data = 0;
 
 
 static void help(const char *progname);
@@ -316,6 +317,7 @@ main(int argc, char **argv)
                {"role", required_argument, NULL, 3},
                {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
                {"no-security-label", no_argument, &no_security_label, 1},
+               {"no-unlogged-table-data", no_argument, &no_unlogged_table_data, 1},
 
                {NULL, 0, NULL, 0}
        };
@@ -466,6 +468,8 @@ main(int argc, char **argv)
                                        use_setsessauth = 1;
                                else if (strcmp(optarg, "no-security-label") == 0)
                                        no_security_label = 1;
+                               else if (strcmp(optarg, "no-unlogged-table-data") == 0)
+                                       no_unlogged_table_data = 1;
                                else
                                {
                                        fprintf(stderr,
@@ -864,6 +868,7 @@ help(const char *progname)
        printf(_("  --quote-all-identifiers     quote all identifiers, even if not keywords\n"));
        printf(_("  --role=ROLENAME             do SET ROLE before dump\n"));
        printf(_("  --no-security-label         do not dump security label assignments\n"));
+       printf(_("  --no-unlogged-table-data    do not dump unlogged table data\n"));
        printf(_("  --use-set-session-authorization\n"
                         "                              use SET SESSION AUTHORIZATION commands instead of\n"
        "                              ALTER OWNER commands to set ownership\n"));
@@ -1471,6 +1476,10 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids)
                /* Skip SEQUENCEs (handled elsewhere) */
                if (tblinfo[i].relkind == RELKIND_SEQUENCE)
                        continue;
+               /* Skip unlogged tables if so requested */
+               if (tblinfo[i].relpersistence == RELPERSISTENCE_UNLOGGED
+                       && no_unlogged_table_data)
+                       continue;
 
                if (tblinfo[i].dobj.dump)
                {
@@ -3447,6 +3456,7 @@ getTables(int *numTables)
        int                     i_relhasrules;
        int                     i_relhasoids;
        int                     i_relfrozenxid;
+       int                     i_relpersistence;
        int                     i_owning_tab;
        int                     i_owning_col;
        int                     i_reltablespace;
@@ -3477,7 +3487,40 @@ getTables(int *numTables)
         * we cannot correctly identify inherited columns, owned sequences, etc.
         */
 
-       if (g_fout->remoteVersion >= 90000)
+       if (g_fout->remoteVersion >= 90100)
+       {
+               /*
+                * Left join to pick up dependency info linking sequences to their
+                * owning column, if any (note this dependency is AUTO as of 8.2)
+                */
+               appendPQExpBuffer(query,
+                                                 "SELECT c.tableoid, c.oid, c.relname, "
+                                                 "c.relacl, c.relkind, c.relnamespace, "
+                                                 "(%s c.relowner) AS rolname, "
+                                                 "c.relchecks, c.relhastriggers, "
+                                                 "c.relhasindex, c.relhasrules, c.relhasoids, "
+                                                 "c.relfrozenxid, c.relpersistence, "
+                                                 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
+                                                 "d.refobjid AS owning_tab, "
+                                                 "d.refobjsubid AS owning_col, "
+                                                 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
+                                               "array_to_string(c.reloptions, ', ') AS reloptions, "
+                                                 "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
+                                                 "FROM pg_class c "
+                                                 "LEFT JOIN pg_depend d ON "
+                                                 "(c.relkind = '%c' AND "
+                                                 "d.classid = c.tableoid AND d.objid = c.oid AND "
+                                                 "d.objsubid = 0 AND "
+                                                 "d.refclassid = c.tableoid AND d.deptype = 'a') "
+                                          "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
+                                                 "WHERE c.relkind in ('%c', '%c', '%c', '%c') "
+                                                 "ORDER BY c.oid",
+                                                 username_subquery,
+                                                 RELKIND_SEQUENCE,
+                                                 RELKIND_RELATION, RELKIND_SEQUENCE,
+                                                 RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
+       }
+       else if (g_fout->remoteVersion >= 90000)
        {
                /*
                 * Left join to pick up dependency info linking sequences to their
@@ -3489,7 +3532,7 @@ getTables(int *numTables)
                                                  "(%s c.relowner) AS rolname, "
                                                  "c.relchecks, c.relhastriggers, "
                                                  "c.relhasindex, c.relhasrules, c.relhasoids, "
-                                                 "c.relfrozenxid, "
+                                                 "c.relfrozenxid, 'p' AS relpersistence, "
                                                  "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
@@ -3522,7 +3565,7 @@ getTables(int *numTables)
                                                  "(%s c.relowner) AS rolname, "
                                                  "c.relchecks, c.relhastriggers, "
                                                  "c.relhasindex, c.relhasrules, c.relhasoids, "
-                                                 "c.relfrozenxid, "
+                                                 "c.relfrozenxid, 'p' AS relpersistence, "
                                                  "NULL AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
@@ -3555,7 +3598,7 @@ getTables(int *numTables)
                                                  "(%s relowner) AS rolname, "
                                                  "relchecks, (reltriggers <> 0) AS relhastriggers, "
                                                  "relhasindex, relhasrules, relhasoids, "
-                                                 "relfrozenxid, "
+                                                 "relfrozenxid, 'p' AS relpersistence, "
                                                  "NULL AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
@@ -3587,7 +3630,7 @@ getTables(int *numTables)
                                                  "(%s relowner) AS rolname, "
                                                  "relchecks, (reltriggers <> 0) AS relhastriggers, "
                                                  "relhasindex, relhasrules, relhasoids, "
-                                                 "0 AS relfrozenxid, "
+                                                 "0 AS relfrozenxid, 'p' AS relpersistence, "
                                                  "NULL AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
@@ -3619,7 +3662,7 @@ getTables(int *numTables)
                                                  "(%s relowner) AS rolname, "
                                                  "relchecks, (reltriggers <> 0) AS relhastriggers, "
                                                  "relhasindex, relhasrules, relhasoids, "
-                                                 "0 AS relfrozenxid, "
+                                                 "0 AS relfrozenxid, 'p' AS relpersistence, "
                                                  "NULL AS reloftype, "
                                                  "d.refobjid AS owning_tab, "
                                                  "d.refobjsubid AS owning_col, "
@@ -3647,7 +3690,7 @@ getTables(int *numTables)
                                                  "(%s relowner) AS rolname, "
                                                  "relchecks, (reltriggers <> 0) AS relhastriggers, "
                                                  "relhasindex, relhasrules, relhasoids, "
-                                                 "0 AS relfrozenxid, "
+                                                 "0 AS relfrozenxid, 'p' AS relpersistence, "
                                                  "NULL AS reloftype, "
                                                  "NULL::oid AS owning_tab, "
                                                  "NULL::int4 AS owning_col, "
@@ -3670,7 +3713,7 @@ getTables(int *numTables)
                                                  "relchecks, (reltriggers <> 0) AS relhastriggers, "
                                                  "relhasindex, relhasrules, "
                                                  "'t'::bool AS relhasoids, "
-                                                 "0 AS relfrozenxid, "
+                                                 "0 AS relfrozenxid, 'p' AS relpersistence, "
                                                  "NULL AS reloftype, "
                                                  "NULL::oid AS owning_tab, "
                                                  "NULL::int4 AS owning_col, "
@@ -3703,7 +3746,7 @@ getTables(int *numTables)
                                                  "relchecks, (reltriggers <> 0) AS relhastriggers, "
                                                  "relhasindex, relhasrules, "
                                                  "'t'::bool AS relhasoids, "
-                                                 "0 as relfrozenxid, "
+                                                 "0 as relfrozenxid, 'p' AS relpersistence, "
                                                  "NULL AS reloftype, "
                                                  "NULL::oid AS owning_tab, "
                                                  "NULL::int4 AS owning_col, "
@@ -3749,6 +3792,7 @@ getTables(int *numTables)
        i_relhasrules = PQfnumber(res, "relhasrules");
        i_relhasoids = PQfnumber(res, "relhasoids");
        i_relfrozenxid = PQfnumber(res, "relfrozenxid");
+       i_relpersistence = PQfnumber(res, "relpersistence");
        i_owning_tab = PQfnumber(res, "owning_tab");
        i_owning_col = PQfnumber(res, "owning_col");
        i_reltablespace = PQfnumber(res, "reltablespace");
@@ -3783,6 +3827,7 @@ getTables(int *numTables)
                tblinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
                tblinfo[i].relacl = strdup(PQgetvalue(res, i, i_relacl));
                tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
+               tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
                tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
                tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
                tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
@@ -11051,8 +11096,12 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
                if (binary_upgrade)
                        binary_upgrade_set_relfilenodes(q, tbinfo->dobj.catId.oid, false);
 
-               appendPQExpBuffer(q, "CREATE TABLE %s",
-                                                 fmtId(tbinfo->dobj.name));
+               if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED)
+                       appendPQExpBuffer(q, "CREATE UNLOGGED TABLE %s",
+                                                         fmtId(tbinfo->dobj.name));
+               else
+                       appendPQExpBuffer(q, "CREATE TABLE %s",
+                                                         fmtId(tbinfo->dobj.name));
                if (tbinfo->reloftype)
                        appendPQExpBuffer(q, " OF %s", tbinfo->reloftype);
                actual_atts = 0;
index 78855357c88a1bf678613e8911914cf0f7372a38..4313fd866bb637a117731bbbe5eccaf3b78fdb0e 100644 (file)
@@ -220,6 +220,7 @@ typedef struct _tableInfo
        char       *rolname;            /* name of owner, or empty string */
        char       *relacl;
        char            relkind;
+       char            relpersistence; /* relation persistence */
        char       *reltablespace;      /* relation tablespace */
        char       *reloptions;         /* options specified by WITH (...) */
        char       *toast_reloptions;           /* ditto, for the TOAST table */
index beeba1cb5286a12a23c8ccff498eca667faf3fc6..ef05a46e7aad0cc6beffd0765cc5c3929bb2fb73 100644 (file)
@@ -70,6 +70,7 @@ static int    inserts = 0;
 static int     no_tablespaces = 0;
 static int     use_setsessauth = 0;
 static int     no_security_label = 0;
+static int     no_unlogged_table_data = 0;
 static int     server_version;
 
 static FILE *OPF;
@@ -135,6 +136,7 @@ main(int argc, char *argv[])
                {"role", required_argument, NULL, 3},
                {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
                {"no-security-label", no_argument, &no_security_label, 1},
+               {"no-unlogged-table-data", no_argument, &no_unlogged_table_data, 1},
 
                {NULL, 0, NULL, 0}
        };
@@ -290,6 +292,8 @@ main(int argc, char *argv[])
                                        use_setsessauth = 1;
                                else if (strcmp(optarg, "no-security-label") == 0)
                                        no_security_label = 1;
+                               else if (strcmp(optarg, "no-unlogged-table-data") == 0)
+                                       no_unlogged_table_data = 1;
                                else
                                {
                                        fprintf(stderr,
@@ -377,6 +381,8 @@ main(int argc, char *argv[])
                appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization");
        if (no_security_label)
                appendPQExpBuffer(pgdumpopts, " --no-security-label");
+       if (no_unlogged_table_data)
+               appendPQExpBuffer(pgdumpopts, " --no-unlogged-table-data");
 
        /*
         * If there was a database specified on the command line, use that,
@@ -574,6 +580,7 @@ help(void)
        printf(_("  --quote-all-identifiers     quote all identifiers, even if not keywords\n"));
        printf(_("  --role=ROLENAME             do SET ROLE before dump\n"));
        printf(_("  --no-security-label         do not dump security label assignments\n"));
+       printf(_("  --no-unlogged-table-data    do not dump unlogged table data\n"));
        printf(_("  --use-set-session-authorization\n"
                         "                              use SET SESSION AUTHORIZATION commands instead of\n"
        "                              ALTER OWNER commands to set ownership\n"));
index edbe882963a4f83e11723223ec2b76f8c9d3adea..406cc838e952638653bbd5d3097c0a260b904d0d 100644 (file)
@@ -1118,6 +1118,7 @@ describeOneTableDetails(const char *schemaname,
                Oid                     tablespace;
                char       *reloptions;
                char       *reloftype;
+               char            relpersistence;
        }                       tableinfo;
        bool            show_modifiers = false;
        bool            retval;
@@ -1133,6 +1134,23 @@ describeOneTableDetails(const char *schemaname,
 
        /* Get general table info */
        if (pset.sversion >= 90000)
+       {
+               printfPQExpBuffer(&buf,
+                         "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
+                                                 "c.relhastriggers, c.relhasoids, "
+                                                 "%s, c.reltablespace, "
+                                                 "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
+                                                 "c.relpersistence\n"
+                                                 "FROM pg_catalog.pg_class c\n "
+                  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
+                                                 "WHERE c.oid = '%s'\n",
+                                                 (verbose ?
+                                                  "pg_catalog.array_to_string(c.reloptions || "
+                                                  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
+                                                  : "''"),
+                                                 oid);
+       }
+       else if (pset.sversion >= 90000)
        {
                printfPQExpBuffer(&buf,
                          "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
@@ -1218,6 +1236,8 @@ describeOneTableDetails(const char *schemaname,
                atooid(PQgetvalue(res, 0, 7)) : 0;
        tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 8), "") != 0) ?
                strdup(PQgetvalue(res, 0, 8)) : 0;
+       tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
+               PQgetvalue(res, 0, 9)[0] : 0;
        PQclear(res);
        res = NULL;
 
@@ -1269,8 +1289,12 @@ describeOneTableDetails(const char *schemaname,
        switch (tableinfo.relkind)
        {
                case 'r':
-                       printfPQExpBuffer(&title, _("Table \"%s.%s\""),
-                                                         schemaname, relationname);
+                       if (tableinfo.relpersistence == 'u')
+                               printfPQExpBuffer(&title, _("Unlogged Table \"%s.%s\""),
+                                                                 schemaname, relationname);
+                       else
+                               printfPQExpBuffer(&title, _("Table \"%s.%s\""),
+                                                                 schemaname, relationname);
                        break;
                case 'v':
                        printfPQExpBuffer(&title, _("View \"%s.%s\""),
@@ -1281,8 +1305,12 @@ describeOneTableDetails(const char *schemaname,
                                                          schemaname, relationname);
                        break;
                case 'i':
-                       printfPQExpBuffer(&title, _("Index \"%s.%s\""),
-                                                         schemaname, relationname);
+                       if (tableinfo.relpersistence == 'u')
+                               printfPQExpBuffer(&title, _("Unlogged Index \"%s.%s\""),
+                                                                 schemaname, relationname);
+                       else
+                               printfPQExpBuffer(&title, _("Index \"%s.%s\""),
+                                                                 schemaname, relationname);
                        break;
                case 's':
                        /* not used as of 8.2, but keep it for backwards compatibility */
index e2d7b450c2acc4cbdb59975470edb526835b4b2f..b1eef9205468d18c9de59b29d2c45812356b26e9 100644 (file)
@@ -389,6 +389,7 @@ extern void ginUpdateStats(Relation index, const GinStatsData *stats);
 
 /* gininsert.c */
 extern Datum ginbuild(PG_FUNCTION_ARGS);
+extern Datum ginbuildempty(PG_FUNCTION_ARGS);
 extern Datum gininsert(PG_FUNCTION_ARGS);
 extern void ginEntryInsert(Relation index, GinState *ginstate,
                           OffsetNumber attnum, Datum value,
index 1bacb468ee634fbecdc9eea25f7c095cfe5d1768..77d679d48996c724be7023c09106fd966ee1977f 100644 (file)
@@ -281,6 +281,7 @@ typedef struct
 
 /* gist.c */
 extern Datum gistbuild(PG_FUNCTION_ARGS);
+extern Datum gistbuildempty(PG_FUNCTION_ARGS);
 extern Datum gistinsert(PG_FUNCTION_ARGS);
 extern MemoryContext createTempGistContext(void);
 extern void initGISTstate(GISTSTATE *giststate, Relation index);
index d5899f4d57da02180a4709ada0db8c23ada76740..a48320bbeed1e10849fbc39d889e09fc6d4bb4d7 100644 (file)
@@ -242,6 +242,7 @@ typedef HashMetaPageData *HashMetaPage;
 /* public routines */
 
 extern Datum hashbuild(PG_FUNCTION_ARGS);
+extern Datum hashbuildempty(PG_FUNCTION_ARGS);
 extern Datum hashinsert(PG_FUNCTION_ARGS);
 extern Datum hashbeginscan(PG_FUNCTION_ARGS);
 extern Datum hashgettuple(PG_FUNCTION_ARGS);
@@ -291,7 +292,7 @@ extern Buffer _hash_addovflpage(Relation rel, Buffer metabuf, Buffer buf);
 extern BlockNumber _hash_freeovflpage(Relation rel, Buffer ovflbuf,
                                   BufferAccessStrategy bstrategy);
 extern void _hash_initbitmap(Relation rel, HashMetaPage metap,
-                                BlockNumber blkno);
+                                BlockNumber blkno, ForkNumber forkNum);
 extern void _hash_squeezebucket(Relation rel,
                                        Bucket bucket, BlockNumber bucket_blkno,
                                        BufferAccessStrategy bstrategy);
@@ -303,7 +304,8 @@ extern void _hash_droplock(Relation rel, BlockNumber whichlock, int access);
 extern Buffer _hash_getbuf(Relation rel, BlockNumber blkno,
                         int access, int flags);
 extern Buffer _hash_getinitbuf(Relation rel, BlockNumber blkno);
-extern Buffer _hash_getnewbuf(Relation rel, BlockNumber blkno);
+extern Buffer _hash_getnewbuf(Relation rel, BlockNumber blkno,
+                               ForkNumber forkNum);
 extern Buffer _hash_getbuf_with_strategy(Relation rel, BlockNumber blkno,
                                                   int access, int flags,
                                                   BufferAccessStrategy bstrategy);
@@ -312,7 +314,8 @@ extern void _hash_dropbuf(Relation rel, Buffer buf);
 extern void _hash_wrtbuf(Relation rel, Buffer buf);
 extern void _hash_chgbufaccess(Relation rel, Buffer buf, int from_access,
                                   int to_access);
-extern uint32 _hash_metapinit(Relation rel, double num_tuples);
+extern uint32 _hash_metapinit(Relation rel, double num_tuples,
+                               ForkNumber forkNum);
 extern void _hash_pageinit(Page page, Size size);
 extern void _hash_expandtable(Relation rel, Buffer metabuf);
 
index 3bbc4d1cda4a653b0ba4f200e1e4d4af9aa72621..283612eaed82aad5316487855cbf60ea47208d75 100644 (file)
@@ -555,6 +555,7 @@ typedef BTScanOpaqueData *BTScanOpaque;
  * prototypes for functions in nbtree.c (external entry points for btree)
  */
 extern Datum btbuild(PG_FUNCTION_ARGS);
+extern Datum btbuildempty(PG_FUNCTION_ARGS);
 extern Datum btinsert(PG_FUNCTION_ARGS);
 extern Datum btbeginscan(PG_FUNCTION_ARGS);
 extern Datum btgettuple(PG_FUNCTION_ARGS);
index 56dcdd53fe2596af646c8f115caa73cdfebbefc3..40cb9ff2e41f6293a0703178145deb5f52bc9957 100644 (file)
@@ -25,7 +25,7 @@
 
 extern const char *forkNames[];
 extern ForkNumber forkname_to_number(char *forkName);
-extern int forkname_chars(const char *str);
+extern int forkname_chars(const char *str, ForkNumber *);
 
 extern char *relpathbackend(RelFileNode rnode, BackendId backend,
                          ForkNumber forknum);
index ca1c3bd683124d6fb67c2ff32eb33032ec33bef4..b44991de80d2026335d911a6c974f6ca92e105a7 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201012271
+#define CATALOG_VERSION_NO     201012291
 
 #endif
index 9425329e948f85a6fbd051872c0f4abacb06c938..1aa43a9e3572d32655cf1353d7082a1e6f9bfe0c 100644 (file)
@@ -60,6 +60,7 @@ CATALOG(pg_am,2601)
        regproc         ammarkpos;              /* "mark current scan position" function */
        regproc         amrestrpos;             /* "restore marked scan position" function */
        regproc         ambuild;                /* "build new index" function */
+       regproc         ambuildempty;   /* "build empty index" function */
        regproc         ambulkdelete;   /* bulk-delete function */
        regproc         amvacuumcleanup;        /* post-VACUUM cleanup function */
        regproc         amcostestimate; /* estimate cost of an indexscan */
@@ -101,26 +102,27 @@ typedef FormData_pg_am *Form_pg_am;
 #define Anum_pg_am_ammarkpos                   21
 #define Anum_pg_am_amrestrpos                  22
 #define Anum_pg_am_ambuild                             23
-#define Anum_pg_am_ambulkdelete                        24
-#define Anum_pg_am_amvacuumcleanup             25
-#define Anum_pg_am_amcostestimate              26
-#define Anum_pg_am_amoptions                   27
+#define Anum_pg_am_ambuildempty                        24
+#define Anum_pg_am_ambulkdelete                        25
+#define Anum_pg_am_amvacuumcleanup             26
+#define Anum_pg_am_amcostestimate              27
+#define Anum_pg_am_amoptions                   28
 
 /* ----------------
  *             initial contents of pg_am
  * ----------------
  */
 
-DATA(insert OID = 403 (  btree 5 1 t f t t t t t t f t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions ));
+DATA(insert OID = 403 (  btree 5 1 t f t t t t t t f t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbuildempty btbulkdelete btvacuumcleanup btcostestimate btoptions ));
 DESCR("b-tree index access method");
 #define BTREE_AM_OID 403
-DATA(insert OID = 405 (  hash  1 1 f f t f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
+DATA(insert OID = 405 (  hash  1 1 f f t f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
 DESCR("hash index access method");
 #define HASH_AM_OID 405
-DATA(insert OID = 783 (  gist  0 8 f t f f t t t t t t 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
+DATA(insert OID = 783 (  gist  0 8 f t f f t t t t t t 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
 DESCR("GiST index access method");
 #define GIST_AM_OID 783
-DATA(insert OID = 2742 (  gin  0 5 f f f f t t f f t f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
+DATA(insert OID = 2742 (  gin  0 5 f f f f t t f f t f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
 DESCR("GIN index access method");
 #define GIN_AM_OID 2742
 
index 1edbfe378b115f133dbf5b1987c95965e2e747c9..39f9743990efbda47f0b47707377a453f60ddc22 100644 (file)
@@ -150,6 +150,7 @@ DESCR("");
 #define                  RELKIND_COMPOSITE_TYPE  'c'           /* composite type */
 
 #define                  RELPERSISTENCE_PERMANENT      'p'
+#define                  RELPERSISTENCE_UNLOGGED       'u'
 #define                  RELPERSISTENCE_TEMP           't'
 
 #endif   /* PG_CLASS_H */
index bc3fd71876e89fb27df236b240f6c9dd4f81ef26..c6242433b7b8cdaa9ea63148f987a2320dafa4a4 100644 (file)
@@ -689,6 +689,8 @@ DATA(insert OID = 337 (  btrestrpos            PGNSP PGUID 12 1 0 0 f f f t f v 1 0 227
 DESCR("btree(internal)");
 DATA(insert OID = 338 (  btbuild                  PGNSP PGUID 12 1 0 0 f f f t f v 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ btbuild _null_ _null_ _null_ ));
 DESCR("btree(internal)");
+DATA(insert OID = 328 (  btbuildempty     PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "2281" _null_ _null_ _null_ _null_ btbuildempty _null_ _null_ _null_ ));
+DESCR("btree(internal)");
 DATA(insert OID = 332 (  btbulkdelete     PGNSP PGUID 12 1 0 0 f f f t f v 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ btbulkdelete _null_ _null_ _null_ ));
 DESCR("btree(internal)");
 DATA(insert OID = 972 (  btvacuumcleanup   PGNSP PGUID 12 1 0 0 f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ btvacuumcleanup _null_ _null_ _null_ ));
@@ -808,6 +810,8 @@ DATA(insert OID = 447 (  hashrestrpos          PGNSP PGUID 12 1 0 0 f f f t f v 1 0 22
 DESCR("hash(internal)");
 DATA(insert OID = 448 (  hashbuild                PGNSP PGUID 12 1 0 0 f f f t f v 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ hashbuild _null_ _null_ _null_ ));
 DESCR("hash(internal)");
+DATA(insert OID = 327 (  hashbuildempty           PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "2281" _null_ _null_ _null_ _null_ hashbuildempty _null_ _null_ _null_ ));
+DESCR("hash(internal)");
 DATA(insert OID = 442 (  hashbulkdelete    PGNSP PGUID 12 1 0 0 f f f t f v 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ hashbulkdelete _null_ _null_ _null_ ));
 DESCR("hash(internal)");
 DATA(insert OID = 425 (  hashvacuumcleanup PGNSP PGUID 12 1 0 0 f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ hashvacuumcleanup _null_ _null_ _null_ ));
@@ -1104,6 +1108,8 @@ DATA(insert OID = 781 (  gistrestrpos        PGNSP PGUID 12 1 0 0 f f f t f v 1 0 22
 DESCR("gist(internal)");
 DATA(insert OID = 782 (  gistbuild                PGNSP PGUID 12 1 0 0 f f f t f v 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ gistbuild _null_ _null_ _null_ ));
 DESCR("gist(internal)");
+DATA(insert OID = 326 (  gistbuildempty           PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "2281" _null_ _null_ _null_ _null_ gistbuildempty _null_ _null_ _null_ ));
+DESCR("gist(internal)");
 DATA(insert OID = 776 (  gistbulkdelete    PGNSP PGUID 12 1 0 0 f f f t f v 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ gistbulkdelete _null_ _null_ _null_ ));
 DESCR("gist(internal)");
 DATA(insert OID = 2561 (  gistvacuumcleanup   PGNSP PGUID 12 1 0 0 f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ gistvacuumcleanup _null_ _null_ _null_ ));
@@ -4353,6 +4359,8 @@ DATA(insert OID = 2737 (  ginrestrpos        PGNSP PGUID 12 1 0 0 f f f t f v 1 0 22
 DESCR("gin(internal)");
 DATA(insert OID = 2738 (  ginbuild                PGNSP PGUID 12 1 0 0 f f f t f v 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ ginbuild _null_ _null_ _null_ ));
 DESCR("gin(internal)");
+DATA(insert OID = 325 (  ginbuildempty    PGNSP PGUID 12 1 0 0 f f f t f v 1 0 2278 "2281" _null_ _null_ _null_ _null_ ginbuildempty _null_ _null_ _null_ ));
+DESCR("gin(internal)");
 DATA(insert OID = 2739 (  ginbulkdelete    PGNSP PGUID 12 1 0 0 f f f t f v 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ ginbulkdelete _null_ _null_ _null_ ));
 DESCR("gin(internal)");
 DATA(insert OID = 2740 (  ginvacuumcleanup PGNSP PGUID 12 1 0 0 f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ ginvacuumcleanup _null_ _null_ _null_ ));
index f086b1c33f673f359592a13419fa0cc17c4fd177..e2a1fecdf566bcde0b9896f5493704ebca7ecfad 100644 (file)
@@ -35,6 +35,8 @@ extern void AtSubCommit_smgr(void);
 extern void AtSubAbort_smgr(void);
 extern void PostPrepare_smgr(void);
 
+extern void log_smgrcreate(RelFileNode *rnode, ForkNumber forkNum);
+
 extern void smgr_redo(XLogRecPtr lsn, XLogRecord *record);
 extern void smgr_desc(StringInfo buf, uint8 xl_info, char *rec);
 
index 09d167a323056f27df47756f17a86c3024d12e82..726daf5c07faa9ba1fe07b0f0facb95602473c3d 100644 (file)
@@ -390,6 +390,7 @@ PG_KEYWORD("union", UNION, RESERVED_KEYWORD)
 PG_KEYWORD("unique", UNIQUE, RESERVED_KEYWORD)
 PG_KEYWORD("unknown", UNKNOWN, UNRESERVED_KEYWORD)
 PG_KEYWORD("unlisten", UNLISTEN, UNRESERVED_KEYWORD)
+PG_KEYWORD("unlogged", UNLOGGED, UNRESERVED_KEYWORD)
 PG_KEYWORD("until", UNTIL, UNRESERVED_KEYWORD)
 PG_KEYWORD("update", UPDATE, UNRESERVED_KEYWORD)
 PG_KEYWORD("user", USER, RESERVED_KEYWORD)
index 5f41adfcc222c6127a9ca417274972f909c587ba..7cf1a643d37b028ce0866badd85eea2eaa64a792 100644 (file)
  * Enable debugging print statements for WAL-related operations; see
  * also the wal_debug GUC var.
  */
-/* #define WAL_DEBUG */
+#define WAL_DEBUG
 
 /*
  * Enable tracing of resource consumption during sort operations;
index 0c18fb52ee2b3b5faf06f1d3b16dec55a4f0c6c5..823ca3276643ee64795a06e849b8e929e4185ebe 100644 (file)
@@ -37,6 +37,7 @@
 #define BM_JUST_DIRTIED                        (1 << 5)                /* dirtied since write started */
 #define BM_PIN_COUNT_WAITER            (1 << 6)                /* have waiter for sole pin */
 #define BM_CHECKPOINT_NEEDED   (1 << 7)                /* must write for checkpoint */
+#define BM_PERMANENT                   (1 << 8)                /* permanent relation (not unlogged) */
 
 typedef bits16 BufFlags;
 
index 8c1552190ce9a8ef50ad3217a7455caa82c7e600..58808f0b592763e71a67ba736de68fb70ec9b013 100644 (file)
@@ -177,13 +177,17 @@ extern void AtEOXact_Buffers(bool isCommit);
 extern void PrintBufferLeakWarning(Buffer buffer);
 extern void CheckPointBuffers(int flags);
 extern BlockNumber BufferGetBlockNumber(Buffer buffer);
-extern BlockNumber RelationGetNumberOfBlocks(Relation relation);
+extern BlockNumber RelationGetNumberOfBlocksInFork(Relation relation,
+                                                               ForkNumber forkNum);
 extern void FlushRelationBuffers(Relation rel);
 extern void FlushDatabaseBuffers(Oid dbid);
 extern void DropRelFileNodeBuffers(RelFileNodeBackend rnode,
                                           ForkNumber forkNum, BlockNumber firstDelBlock);
 extern void DropDatabaseBuffers(Oid dbid);
 
+#define RelationGetNumberOfBlocks(reln) \
+       RelationGetNumberOfBlocksInFork(reln, MAIN_FORKNUM)
+
 #ifdef NOT_USED
 extern void PrintPinnedBufs(void);
 #endif
index b24a98c83eeaed88d674232f600ebb2d68cb4607..7c577240f8bcac3f2e44d967c084d9ae13fc644e 100644 (file)
@@ -14,5 +14,6 @@
 #define COPYDIR_H
 
 extern void copydir(char *fromdir, char *todir, bool recurse);
+extern void copy_file(char *fromfile, char *tofile);
 
 #endif   /* COPYDIR_H */
diff --git a/src/include/storage/reinit.h b/src/include/storage/reinit.h
new file mode 100644 (file)
index 0000000..9999dff
--- /dev/null
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * reinit.h
+ *       Reinitialization of unlogged relations
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/fd.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef REINIT_H
+#define REINIT_H
+
+extern void ResetUnloggedRelations(int op);
+
+#define UNLOGGED_RELATION_CLEANUP              0x0001
+#define UNLOGGED_RELATION_INIT                 0x0002
+
+#endif   /* REINIT_H */
index 24a72e60acb76e9c94f4d6ca5966c0450c8e3a36..f71b2331a5364adaf8cf7696cfb64fec2005e471 100644 (file)
@@ -27,7 +27,8 @@ typedef enum ForkNumber
        InvalidForkNumber = -1,
        MAIN_FORKNUM = 0,
        FSM_FORKNUM,
-       VISIBILITYMAP_FORKNUM
+       VISIBILITYMAP_FORKNUM,
+       INIT_FORKNUM    
 
        /*
         * NOTE: if you add a new fork, change MAX_FORKNUM below and update the
@@ -35,7 +36,7 @@ typedef enum ForkNumber
         */
 } ForkNumber;
 
-#define MAX_FORKNUM            VISIBILITYMAP_FORKNUM
+#define MAX_FORKNUM            INIT_FORKNUM
 
 /*
  * RelFileNode must provide all that we need to know to physically access
index 88a3168d133b31f4e468fb3e13a5f0e4d71c7749..d5b5e58de3b5fd40e9fed095c8f66c93195a6e66 100644 (file)
@@ -114,6 +114,7 @@ typedef struct RelationAmInfo
        FmgrInfo        ammarkpos;
        FmgrInfo        amrestrpos;
        FmgrInfo        ambuild;
+       FmgrInfo        ambuildempty;
        FmgrInfo        ambulkdelete;
        FmgrInfo        amvacuumcleanup;
        FmgrInfo        amcostestimate;