I attach a version of my toast-slicing patch, against current CVS
authorBruce Momjian <bruce@momjian.us>
Tue, 5 Mar 2002 05:33:31 +0000 (05:33 +0000)
committerBruce Momjian <bruce@momjian.us>
Tue, 5 Mar 2002 05:33:31 +0000 (05:33 +0000)
(current as of a few hours ago.)

This patch:

1. Adds PG_GETARG_xxx_P_SLICE() macros and associated support routines.

2. Adds routines in src/backend/access/tuptoaster.c for fetching only
necessary chunks of a toasted value. (Modelled on latest changes to
assume chunks are returned in order).

3. Amends text_substr and bytea_substr to use new methods. It now
handles multibyte cases -and should still lead to a performance
improvement in the multibyte case where the substring is near the
beginning of the string.

4. Added new command: ALTER TABLE tabname ALTER COLUMN colname SET
STORAGE {PLAIN | EXTERNAL | EXTENDED | MAIN} to parser and documented in
alter-table.sgml. (NB I used ColId as the item type for the storage
mode string, rather than a new production - I hope this makes sense!).
All this does is sets attstorage for the specified column.

4. AlterTableAlterColumnStatistics is now AlterTableAlterColumnFlags and
handles both statistics and storage (it uses the subtype code to
distinguish). The previous version of my patch also re-arranged other
code in backend/commands/command.c but I have dropped that from this
patch.(I plan to return to it separately).

5. Documented new macros (and also the PG_GETARG_xxx_P_COPY macros) in
xfunc.sgml. ref/alter_table.sgml also contains documentation for ALTER
COLUMN SET STORAGE.

John Gray

13 files changed:
doc/src/sgml/ref/alter_table.sgml
doc/src/sgml/xfunc.sgml
src/backend/access/heap/tuptoaster.c
src/backend/commands/command.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/tcop/utility.c
src/backend/utils/adt/varlena.c
src/backend/utils/fmgr/fmgr.c
src/include/access/tuptoaster.h
src/include/commands/command.h
src/include/fmgr.h
src/include/nodes/parsenodes.h

index dfdd0d3c584d751d9c3845e240f0f2759fae2b03..d5652b44aedf6e977b11c93dfa72b86261e45e3e 100644 (file)
@@ -30,6 +30,8 @@ ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
     class="PARAMETER">value</replaceable> | DROP DEFAULT }
 ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS <replaceable class="PARAMETER">integer</replaceable>
+ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
+    ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STORAGE {PLAIN | EXTERNAL | EXTENDED | MAIN}
 ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
     RENAME [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> TO <replaceable
     class="PARAMETER">newcolumn</replaceable>
@@ -169,6 +171,17 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
    The <literal>ALTER COLUMN SET STATISTICS</literal> form allows you to
    set the statistics-gathering target for subsequent
    <xref linkend="sql-analyze" endterm="sql-analyze-title"> operations.
+   The <literal>ALTER COLUMN SET STORAGE</literal> form allows the
+   column storage mode to be set. This controls whether this column is
+   held inline or in a supplementary table, and whether the data
+   should be compressed or not. <literal>PLAIN</literal> must be used
+   for fixed-length values such as <literal>INTEGER</literal> and is
+   inline, uncompressed. <literal>MAIN</literal> is for inline,
+   compressible data. <literal>EXTERNAL</literal> is for external,
+   uncompressed data and <literal>EXTENDED</literal> is for external,
+   compressed data. The use of <literal>EXTERNAL</literal> will make
+   substring operations on a column faster, at the penalty of
+   increased storage space.
    The <literal>RENAME</literal> clause causes the name of a table,
    column, index, or sequence to change without changing any of the
    data. The data will remain of the same type and size after the
index 4f24458fb8ec61c5ad1ef6526435dc5f2bcdb049..f7900413232d068e0f280b7af9b2ca7a15f9c359 100644 (file)
@@ -1296,6 +1296,35 @@ concat_text(PG_FUNCTION_ARGS)
      this works in both strict and nonstrict functions.
     </para>
 
+    <para>
+    Other options provided in the new-style interface are two
+     variants of the
+     <function>PG_GETARG_<replaceable>xxx</replaceable>()</function>
+     macros. The first of these,
+     <function>PG_GETARG_<replaceable>xxx</replaceable>_COPY()</function>
+     guarantees to return a copy of the specified parameter which is
+     safe for writing into. (The normal macros will sometimes return a
+     pointer to the value which must not be written to. Using the
+     <function>PG_GETARG_<replaceable>xxx</replaceable>_COPY()</function>
+     macros guarantees a writable result.)
+    </para>
+
+    <para>
+    The second variant consists of the
+    <function>PG_GETARG_<replaceable>xxx</replaceable>_SLICE()</function>
+    macros which take three parameters. The first is the number of the
+    parameter (as above). The second and third are the offset and
+    length of the segment to be returned. Offsets are counted from
+    zero, and a negative length requests that the remainder of the
+    value be returned. These routines provide more efficient access to
+    parts of large values in the case where they have storage type
+    "external". (The storage type of a column can be specified using
+    <command>ALTER TABLE <repaceable>tablename</replaceable> ALTER
+    COLUMN <replaceable>colname</replaceable> SET STORAGE
+    <replaceable>storagetype</replaceable>. Storage type is one of
+    plain, external, extended or main.)
+    </para>
+
     <para>
      The version-1 function call conventions make it possible to
      return <quote>set</quote> results and implement trigger functions and
index b857f8108732904ffcd9b991f75d40305c4a8f44..ad8212b47732a441056a355bbb662b48a68d379c 100644 (file)
@@ -47,6 +47,8 @@ static void toast_insert_or_update(Relation rel, HeapTuple newtup,
                                           HeapTuple oldtup);
 static Datum toast_save_datum(Relation rel, Datum value);
 static varattrib *toast_fetch_datum(varattrib *attr);
+static varattrib *toast_fetch_datum_slice(varattrib *attr,
+                                                                                 int32 sliceoffset, int32 length);
 
 
 /* ----------
@@ -162,6 +164,80 @@ heap_tuple_untoast_attr(varattrib *attr)
 }
 
 
+/* ----------
+ * heap_tuple_untoast_attr_slice -
+ *
+ *      Public entry point to get back part of a toasted value 
+ *      from compression or external storage.
+ * ----------
+ */
+varattrib  *
+heap_tuple_untoast_attr_slice(varattrib *attr, int32 sliceoffset, int32 slicelength)
+{
+       varattrib  *preslice;
+       varattrib  *result;
+       int32  attrsize;
+       
+       if (VARATT_IS_COMPRESSED(attr))
+       {
+               varattrib *tmp;
+               
+               if (VARATT_IS_EXTERNAL(attr))
+               {
+                       tmp = toast_fetch_datum(attr);
+               }
+               else
+               {
+                       tmp = attr; /* compressed in main tuple */
+               }
+               
+               preslice = (varattrib *) palloc(attr->va_content.va_external.va_rawsize
+                                                                               + VARHDRSZ);
+               VARATT_SIZEP(preslice) = attr->va_content.va_external.va_rawsize + VARHDRSZ;
+               pglz_decompress((PGLZ_Header *) tmp, VARATT_DATA(preslice));
+               
+               if (tmp != attr) 
+                       pfree(tmp);
+       }
+       else    
+       {
+               /* Plain value */
+               if (VARATT_IS_EXTERNAL(attr))
+               {   
+                       /* fast path */
+                       return (toast_fetch_datum_slice(attr, sliceoffset, slicelength));
+               }
+               else
+               {
+                       preslice = attr;
+               }
+       }
+       
+       /* slicing of datum for compressed cases and plain value */
+       
+       attrsize = VARSIZE(preslice) - VARHDRSZ;
+       if (sliceoffset >= attrsize) 
+       {
+               sliceoffset = 0;
+               slicelength = 0;
+       }
+       
+       if (((sliceoffset + slicelength) > attrsize) || slicelength < 0)
+       {
+               slicelength = attrsize - sliceoffset;
+       }
+       
+       result = (varattrib *) palloc(slicelength + VARHDRSZ);
+       VARATT_SIZEP(result) = slicelength + VARHDRSZ;
+       
+       memcpy(VARDATA(result), VARDATA(preslice) + sliceoffset, slicelength);
+       
+       if (preslice != attr) pfree(preslice);
+       
+       return result;
+}
+
+
 /* ----------
  * toast_raw_datum_size -
  *
@@ -981,7 +1057,7 @@ toast_fetch_datum(varattrib *attr)
                VARATT_SIZEP(result) |= VARATT_FLAG_COMPRESSED;
 
        /*
-        * Open the toast relation and it's index
+        * Open the toast relation and its index
         */
        toastrel = heap_open(attr->va_content.va_external.va_toastrelid,
                                                 AccessShareLock);
@@ -1081,4 +1157,198 @@ toast_fetch_datum(varattrib *attr)
        return result;
 }
 
+/* ----------
+ * toast_fetch_datum_slice -
+ *
+ *     Reconstruct a segment of a varattrib from the chunks saved
+ *     in the toast relation
+ * ----------
+ */
+static varattrib *
+toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
+{
+       Relation        toastrel;
+       Relation        toastidx;
+       ScanKeyData toastkey[3];
+       IndexScanDesc toastscan;
+       HeapTupleData toasttup;
+       HeapTuple       ttup;
+       TupleDesc       toasttupDesc;
+       RetrieveIndexResult indexRes;
+       Buffer          buffer;
+
+       varattrib  *result;
+       int32           attrsize;
+       int32       nscankeys;
+       int32           residx;
+       int32       nextidx;
+       int                 numchunks;
+       int                 startchunk;
+       int                 endchunk;
+       int32           startoffset;
+       int32           endoffset;
+       int         totalchunks;
+       Pointer         chunk;
+       bool            isnull;
+       int32           chunksize;
+       int32       chcpystrt;
+       int32       chcpyend;
+
+       attrsize = attr->va_content.va_external.va_extsize;
+       totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
+
+       if (sliceoffset >= attrsize) 
+         {
+           sliceoffset = 0;
+           length = 0;
+         }
+
+       if (((sliceoffset + length) > attrsize) || length < 0)
+         {
+           length = attrsize - sliceoffset;
+         }
+
+       result = (varattrib *) palloc(length + VARHDRSZ);
+       VARATT_SIZEP(result) = length + VARHDRSZ;
+
+       if (VARATT_IS_COMPRESSED(attr))
+               VARATT_SIZEP(result) |= VARATT_FLAG_COMPRESSED;
+       
+       if (length == 0) return (result); /* Can save a lot of work at this point! */
+
+       startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE;
+       endchunk = (sliceoffset + length - 1) / TOAST_MAX_CHUNK_SIZE;
+       numchunks = (endchunk - startchunk ) + 1;
+       startoffset = sliceoffset % TOAST_MAX_CHUNK_SIZE;
+       endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE;
+
+       /*
+        * Open the toast relation and it's index
+        */
+       toastrel = heap_open(attr->va_content.va_external.va_toastrelid,
+                                                AccessShareLock);
+       toasttupDesc = toastrel->rd_att;
+       toastidx = index_open(toastrel->rd_rel->reltoastidxid);
+
+       /*
+        * Setup a scan key to fetch from the index. This is either two keys
+        * or three depending on the number of chunks.
+        */
+       ScanKeyEntryInitialize(&toastkey[0],
+                                                  (bits16) 0,
+                                                  (AttrNumber) 1,
+                                                  (RegProcedure) F_OIDEQ,
+                                                  ObjectIdGetDatum(attr->va_content.va_external.va_valueid));
+       /*
+        * Now dependent on number of chunks:
+        */
+       
+       if (numchunks == 1) 
+       {
+           ScanKeyEntryInitialize(&toastkey[1],
+                                                          (bits16) 0,
+                                                          (AttrNumber) 2,
+                                                          (RegProcedure) F_INT4EQ,
+                                                          Int32GetDatum(startchunk));
+           nscankeys = 2;
+       }
+       else
+       {
+           ScanKeyEntryInitialize(&toastkey[1],
+                                                          (bits16) 0,
+                                                          (AttrNumber) 2,
+                                                          (RegProcedure) F_INT4GE,
+                                                          Int32GetDatum(startchunk));
+           ScanKeyEntryInitialize(&toastkey[2],
+                                                          (bits16) 0,
+                                                          (AttrNumber) 2,
+                                                          (RegProcedure) F_INT4LE,
+                                                          Int32GetDatum(endchunk));
+           nscankeys = 3;
+       }
+
+       /*
+        * Read the chunks by index
+        *
+        * The index is on (valueid, chunkidx) so they will come in order
+        */
+       nextidx = startchunk;
+       toastscan = index_beginscan(toastidx, false, nscankeys, &toastkey[0]);
+       while ((indexRes = index_getnext(toastscan, ForwardScanDirection)) != NULL)
+       {
+               toasttup.t_self = indexRes->heap_iptr;
+               heap_fetch(toastrel, SnapshotToast, &toasttup, &buffer, toastscan);
+               pfree(indexRes);
+
+               if (toasttup.t_data == NULL)
+                       continue;
+               ttup = &toasttup;
+
+               /*
+                * Have a chunk, extract the sequence number and the data
+                */
+               residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull));
+               Assert(!isnull);
+               chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull));
+               Assert(!isnull);
+               chunksize = VARATT_SIZE(chunk) - VARHDRSZ;
+
+               /*
+                * Some checks on the data we've found
+                */
+               if ((residx != nextidx) || (residx > endchunk) || (residx < startchunk))
+                       elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u",
+                                residx, nextidx,
+                                attr->va_content.va_external.va_valueid);
+               if (residx < totalchunks - 1)
+               {
+                       if (chunksize != TOAST_MAX_CHUNK_SIZE)
+                               elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u",
+                                        chunksize, residx,
+                                        attr->va_content.va_external.va_valueid);
+               }
+               else
+               {
+                       if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != attrsize)
+                               elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u",
+                                        chunksize, residx,
+                                        attr->va_content.va_external.va_valueid);
+               }
+
+               /*
+                * Copy the data into proper place in our result
+                */
+               chcpystrt = 0;
+               chcpyend = chunksize - 1;
+               if (residx == startchunk) chcpystrt = startoffset;
+               if (residx == endchunk) chcpyend = endoffset;
+               
+               memcpy(((char *) VARATT_DATA(result)) + 
+                      (residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) +chcpystrt,
+                          VARATT_DATA(chunk) + chcpystrt,
+                          (chcpyend - chcpystrt) + 1);
+               
+               ReleaseBuffer(buffer);
+               nextidx++;
+       }
+
+       /*
+        * Final checks that we successfully fetched the datum
+        */
+       if ( nextidx != (endchunk + 1))
+               elog(ERROR, "missing chunk number %d for toast value %u",
+                        nextidx,
+                        attr->va_content.va_external.va_valueid);
+
+       /*
+        * End scan and close relations
+        */
+       index_endscan(toastscan);
+       index_close(toastidx);
+       heap_close(toastrel, AccessShareLock);
+
+       return result;
+}
+
 #endif   /* TUPLE_TOASTER_ACTIVE */
index f9f10ad4eda1c28f4043b18140d38f789e1a92bf..f3473c5aea33b69029c2d2ad6d7476a422534bbe 100644 (file)
@@ -714,20 +714,27 @@ drop_default(Oid relid, int16 attnum)
 
 
 /*
- * ALTER TABLE ALTER COLUMN SET STATISTICS
+ * ALTER TABLE ALTER COLUMN SET STATISTICS / STORAGE
  */
 void
-AlterTableAlterColumnStatistics(const char *relationName,
+AlterTableAlterColumnFlags(const char *relationName,
                                                                bool inh, const char *colName,
-                                                               Node *statsTarget)
+                                                               Node *flagValue, const char *flagType)
 {
        Relation        rel;
        Oid                     myrelid;
-       int                     newtarget;
+       int                     newtarget = 1;
+       char        newstorage = 'x';
+       char        *storagemode;
        Relation        attrelation;
        HeapTuple       tuple;
 
-       /* we allow this on system tables */
+       /* we allow statistics case for system tables */
+
+       if (*flagType =='M' && !allowSystemTableMods && IsSystemRelationName(relationName))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+                        relationName);
+
 #ifndef NO_SECURITY
        if (!pg_ownercheck(GetUserId(), relationName, RELNAME))
                elog(ERROR, "ALTER TABLE: permission denied");
@@ -742,6 +749,50 @@ AlterTableAlterColumnStatistics(const char *relationName,
        myrelid = RelationGetRelid(rel);
        heap_close(rel, NoLock);        /* close rel, but keep lock! */
 
+       
+       /*
+        * Check the supplied parameters before anything else
+        */
+       if (*flagType == 'S')           /*
+                                                                        * STATISTICS
+                                                                        */
+       {
+               Assert(IsA(flagValue, Integer));
+               newtarget = intVal(flagValue);
+               
+               /*
+                * Limit target to sane range (should we raise an error instead?)
+                */
+               if (newtarget < 0)
+                       newtarget = 0;
+               else if (newtarget > 1000)
+                       newtarget = 1000;
+       }
+       else if (*flagType == 'M')      /*
+                                                                        * STORAGE
+                                                                        */
+       {
+               Assert(IsA(flagValue, Value));
+               
+               storagemode = strVal(flagValue);
+               if (strcasecmp(storagemode, "plain") == 0)
+                       newstorage = 'p';
+               else if (strcasecmp(storagemode, "external") == 0)
+                       newstorage = 'e';
+               else if (strcasecmp(storagemode, "extended") == 0)
+                       newstorage = 'x';
+               else if (strcasecmp(storagemode, "main") == 0)
+                       newstorage = 'm';
+               else
+                       elog(ERROR, "ALTER TABLE: \"%s\" storage not recognized",
+                                storagemode);
+       }
+       else
+       {
+               elog(ERROR, "ALTER TABLE: Invalid column flag: %c",
+                        (int) *flagType);
+       }
+
        /*
         * Propagate to children if desired
         */
@@ -765,23 +816,14 @@ AlterTableAlterColumnStatistics(const char *relationName,
                        if (childrelid == myrelid)
                                continue;
                        rel = heap_open(childrelid, AccessExclusiveLock);
-                       AlterTableAlterColumnStatistics(RelationGetRelationName(rel),
-                                                                                       false, colName, statsTarget);
+                       AlterTableAlterColumnFlags(RelationGetRelationName(rel),
+                                                                                       false, colName, flagValue, flagType);
                        heap_close(rel, AccessExclusiveLock);
                }
        }
 
        /* -= now do the thing on this relation =- */
 
-       Assert(IsA(statsTarget, Integer));
-       newtarget = intVal(statsTarget);
-
-       /* Limit target to sane range (should we raise an error instead?) */
-       if (newtarget < 0)
-               newtarget = 0;
-       else if (newtarget > 1000)
-               newtarget = 1000;
-
        attrelation = heap_openr(AttributeRelationName, RowExclusiveLock);
 
        tuple = SearchSysCacheCopy(ATTNAME,
@@ -795,9 +837,22 @@ AlterTableAlterColumnStatistics(const char *relationName,
        if (((Form_pg_attribute) GETSTRUCT(tuple))->attnum < 0)
                elog(ERROR, "ALTER TABLE: cannot change system attribute \"%s\"",
                         colName);
-
-       ((Form_pg_attribute) GETSTRUCT(tuple))->attstattarget = newtarget;
-
+       /*
+        * Now change the appropriate field
+        */
+       if (*flagType == 'S')
+               ((Form_pg_attribute) GETSTRUCT(tuple))->attstattarget = newtarget;
+       else
+       {
+               if ((newstorage == 'p') ||
+                       (((Form_pg_attribute) GETSTRUCT(tuple))->attlen == -1))
+                       ((Form_pg_attribute) GETSTRUCT(tuple))->attstorage = newstorage;
+               else
+               {
+                       elog(ERROR,
+                                "ALTER TABLE: Fixed-length columns can only have storage \"plain\"");
+               }
+       }
        simple_heap_update(attrelation, &tuple->t_self, tuple);
 
        /* keep system catalog indices current */
index cfbc84d7c47f92e3ddfa6a0ec4cb300b572c3ac0..69a8b9cc873a7c9c4c06eac27d652280b9767874 100644 (file)
@@ -364,7 +364,7 @@ static void doNegateFloat(Value *v);
                OFFSET, OIDS, OPERATOR, OWNER, PASSWORD, PROCEDURAL,
                REINDEX, RENAME, RESET, RETURNS, ROW, RULE,
                SEQUENCE, SETOF, SHARE, SHOW, START, STATEMENT,
-               STATISTICS, STDIN, STDOUT, SYSID,
+               STATISTICS, STDIN, STDOUT, STORAGE, SYSID,
                TEMP, TEMPLATE, TOAST, TRUNCATE, TRUSTED, 
                UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
 
@@ -1117,6 +1117,17 @@ AlterTableStmt:
                                        n->def = (Node *) makeInteger($9);
                                        $$ = (Node *)n;
                                }
+/* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET STORAGE <storagemode> */
+        | ALTER TABLE relation_expr ALTER opt_column ColId SET STORAGE ColId
+                {
+                                       AlterTableStmt *n = makeNode(AlterTableStmt);
+                                       n->subtype = 'M';
+                                       n->relname = $3->relname;
+                                       n->inhOpt = $3->inhOpt;
+                                       n->name = $6;
+                                       n->def = (Node *) makeString($9);
+                                       $$ = (Node *)n;
+                               }
 /* ALTER TABLE <relation> DROP [COLUMN] <colname> {RESTRICT|CASCADE} */
                | ALTER TABLE relation_expr DROP opt_column ColId drop_behavior
                                {
@@ -5959,6 +5970,7 @@ unreserved_keyword:
                | STATISTICS                                    { $$ = "statistics"; }
                | STDIN                                                 { $$ = "stdin"; }
                | STDOUT                                                { $$ = "stdout"; }
+        | STORAGE                       { $$ = "storage"; }
                | SYSID                                                 { $$ = "sysid"; }
                | TEMP                                                  { $$ = "temp"; }
                | TEMPLATE                                              { $$ = "template"; }
index a267e7f135243121a6fff696d3d8ba13270f8c5f..950ef58d02f536c2a67edd67f520cdafa7254fa4 100644 (file)
@@ -243,6 +243,7 @@ static ScanKeyword ScanKeywords[] = {
        {"statistics", STATISTICS},
        {"stdin", STDIN},
        {"stdout", STDOUT},
+       {"storage", STORAGE},
        {"substring", SUBSTRING},
        {"sysid", SYSID},
        {"table", TABLE},
index 40f3cf4591fb95fa8ba3b546ac7ea07ca2d668d5..5384d0ffdbaa54631337f0c172da6e2a1bf6ef7c 100644 (file)
@@ -425,10 +425,12 @@ ProcessUtility(Node *parsetree,
                                                                                                         stmt->def);
                                                break;
                                        case 'S':       /* ALTER COLUMN STATISTICS */
-                                               AlterTableAlterColumnStatistics(stmt->relname,
+                                       case 'M':   /* ALTER COLUMN STORAGE */
+                                               AlterTableAlterColumnFlags(stmt->relname,
                                                                                interpretInhOption(stmt->inhOpt),
                                                                                                                stmt->name,
-                                                                                                               stmt->def);
+                                                                                                               stmt->def,
+                                                                                                       &(stmt->subtype));
                                                break;
                                        case 'D':       /* DROP COLUMN */
                                                AlterTableDropColumn(stmt->relname,
index d81ade7f324d932b727f7c609fbbfd911001c2bf..e3abb87c3f9d22ef0cc681e472be4c003b4a4ee0 100644 (file)
@@ -332,49 +332,71 @@ textcat(PG_FUNCTION_ARGS)
  * Changed behavior if starting position is less than one to conform to SQL92 behavior.
  * Formerly returned the entire string; now returns a portion.
  * - Thomas Lockhart 1998-12-10
+ * Now uses faster TOAST-slicing interface
+ * - John Gray 2002-02-22
  */
 Datum
 text_substr(PG_FUNCTION_ARGS)
 {
-       text       *string = PG_GETARG_TEXT_P(0);
+       text       *string;
        int32           m = PG_GETARG_INT32(1);
        int32           n = PG_GETARG_INT32(2);
-       text       *ret;
-       int                     len;
-
+       int32       sm;
+       int32       sn;
+       int         eml = 1;
 #ifdef MULTIBYTE
        int                     i;
+       int                     len;
+       text       *ret;
        char       *p;
-#endif
-
-       len = VARSIZE(string) - VARHDRSZ;
-#ifdef MULTIBYTE
-       len = pg_mbstrlen_with_len(VARDATA(string), len);
-#endif
-
-       /* starting position after the end of the string? */
-       if (m > len)
-       {
-               m = 1;
-               n = 0;
-       }
+#endif 
 
        /*
         * starting position before the start of the string? then offset into
         * the string per SQL92 spec...
         */
-       else if (m < 1)
+       if (m < 1)
        {
                n += (m - 1);
                m = 1;
        }
+       /* Check for m > octet length is made in TOAST access routine */
 
        /* m will now become a zero-based starting position */
+       sm = m - 1;
+       sn = n;
+
+#ifdef MULTIBYTE
+       eml = pg_database_encoding_max_length ();
+
+       if (eml > 1)
+       {
+               sm = 0;
+               sn = (m + n) * eml + 3; /* +3 to avoid mb characters overhanging slice end */
+       }
+#endif 
+
+       string = PG_GETARG_TEXT_P_SLICE (0, sm, sn);
+
+       if (eml == 1) 
+       {
+               PG_RETURN_TEXT_P (string);
+       }
+#ifndef MULTIBYTE
+       PG_RETURN_NULL();   /* notreached: suppress compiler warning */
+#endif
+#ifdef MULTIBYTE
+       len = pg_mbstrlen_with_len (VARDATA (string), sn - 3);
+
+       if (m > len)
+       {
+               m = 1;
+               n = 0;
+       }
        m--;
        if (((m + n) > len) || (n < 0))
                n = (len - m);
 
-#ifdef MULTIBYTE
        p = VARDATA(string);
        for (i = 0; i < m; i++)
                p += pg_mblen(p);
@@ -382,7 +404,6 @@ text_substr(PG_FUNCTION_ARGS)
        for (i = 0; i < n; i++)
                p += pg_mblen(p);
        n = p - (VARDATA(string) + m);
-#endif
 
        ret = (text *) palloc(VARHDRSZ + n);
        VARATT_SIZEP(ret) = VARHDRSZ + n;
@@ -390,6 +411,7 @@ text_substr(PG_FUNCTION_ARGS)
        memcpy(VARDATA(ret), VARDATA(string) + m, n);
 
        PG_RETURN_TEXT_P(ret);
+#endif
 }
 
 /*
@@ -740,26 +762,14 @@ byteacat(PG_FUNCTION_ARGS)
 Datum
 bytea_substr(PG_FUNCTION_ARGS)
 {
-       bytea      *string = PG_GETARG_BYTEA_P(0);
        int32           m = PG_GETARG_INT32(1);
        int32           n = PG_GETARG_INT32(2);
-       bytea      *ret;
-       int                     len;
-
-       len = VARSIZE(string) - VARHDRSZ;
-
-       /* starting position after the end of the string? */
-       if (m > len)
-       {
-               m = 1;
-               n = 0;
-       }
 
        /*
         * starting position before the start of the string? then offset into
         * the string per SQL92 spec...
         */
-       else if (m < 1)
+       if (m < 1)
        {
                n += (m - 1);
                m = 1;
@@ -767,15 +777,8 @@ bytea_substr(PG_FUNCTION_ARGS)
 
        /* m will now become a zero-based starting position */
        m--;
-       if (((m + n) > len) || (n < 0))
-               n = (len - m);
-
-       ret = (bytea *) palloc(VARHDRSZ + n);
-       VARATT_SIZEP(ret) = VARHDRSZ + n;
-
-       memcpy(VARDATA(ret), VARDATA(string) + m, n);
 
-       PG_RETURN_BYTEA_P(ret);
+       PG_RETURN_BYTEA_P(PG_GETARG_BYTEA_P_SLICE (0, m, n));
 }
 
 /*
index e6f953dcc3d9fa395db9d0479654084622f13fb2..9fc6f36a5e91f72d10d05fd14906106fccfbc496 100644 (file)
@@ -1520,3 +1520,10 @@ pg_detoast_datum_copy(struct varlena * datum)
                return result;
        }
 }
+
+struct varlena *
+pg_detoast_datum_slice(struct varlena * datum, int32 first, int32 count)
+{
+       /* Only get the specified portion from the toast rel */
+       return (struct varlena *) heap_tuple_untoast_attr_slice((varattrib *) datum, first, count);
+}
index 71a066f30eefa7f31adead570d4a4d51b6eb1b85..006b1163c36b1da20dbf38070555db08d814457f 100644 (file)
@@ -99,6 +99,17 @@ extern varattrib *heap_tuple_fetch_attr(varattrib *attr);
  */
 extern varattrib *heap_tuple_untoast_attr(varattrib *attr);
 
+/* ----------
+ * heap_tuple_untoast_attr_slice() -
+ *
+ *      Fetches only the specified portion of an attribute.
+ *      (Handles all cases for attribute storage)
+ * ----------
+ */
+extern varattrib *heap_tuple_untoast_attr_slice(varattrib *attr, 
+                                                                                               int32 sliceoffset,
+                                                                                               int32 slicelength);
+
 /* ----------
  * toast_compress_datum -
  *
index 418e094cede05fc5b963b7a9bc2202ab22e0bf17..b1285f7f25be5ec81dcca4fb74ae54293e31c1e0 100644 (file)
@@ -47,9 +47,9 @@ extern void AlterTableAlterColumnDefault(const char *relationName,
                                                         bool inh, const char *colName,
                                                         Node *newDefault);
 
-extern void AlterTableAlterColumnStatistics(const char *relationName,
+extern void AlterTableAlterColumnFlags(const char *relationName,
                                                                bool inh, const char *colName,
-                                                               Node *statsTarget);
+                                                               Node *flagValue, const char *flagType);
 
 extern void AlterTableDropColumn(const char *relationName,
                                         bool inh, const char *colName,
index 49a2d99fcb3260d3a24e514b31beb5b23f3fbf5e..5a4f1ab124006738927a946dfb83ecb65e92e885 100644 (file)
@@ -135,11 +135,16 @@ extern void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo,
  */
 extern struct varlena *pg_detoast_datum(struct varlena * datum);
 extern struct varlena *pg_detoast_datum_copy(struct varlena * datum);
+extern struct varlena *pg_detoast_datum_slice(struct varlena * datum, 
+                                                                                         int32 first, int32 count); 
 
 #define PG_DETOAST_DATUM(datum) \
        pg_detoast_datum((struct varlena *) DatumGetPointer(datum))
 #define PG_DETOAST_DATUM_COPY(datum) \
        pg_detoast_datum_copy((struct varlena *) DatumGetPointer(datum))
+#define PG_DETOAST_DATUM_SLICE(datum,f,c) \
+        pg_detoast_datum_slice((struct varlena *) DatumGetPointer(datum), \
+        (int32) f, (int32) c)
 
 /*
  * Support for cleaning up detoasted copies of inputs. This must only
@@ -187,6 +192,11 @@ extern struct varlena *pg_detoast_datum_copy(struct varlena * datum);
 #define DatumGetTextPCopy(X)           ((text *) PG_DETOAST_DATUM_COPY(X))
 #define DatumGetBpCharPCopy(X)         ((BpChar *) PG_DETOAST_DATUM_COPY(X))
 #define DatumGetVarCharPCopy(X)                ((VarChar *) PG_DETOAST_DATUM_COPY(X))
+/* Variants which return n bytes starting at pos. m */
+#define DatumGetByteaPSlice(X,m,n)  ((bytea *) PG_DETOAST_DATUM_SLICE(X,m,n))
+#define DatumGetTextPSlice(X,m,n)   ((text *) PG_DETOAST_DATUM_SLICE(X,m,n))
+#define DatumGetBpCharPSlice(X,m,n) ((BpChar *) PG_DETOAST_DATUM_SLICE(X,m,n))
+#define DatumGetVarCharPSlice(X,m,n) ((VarChar *) PG_DETOAST_DATUM_SLICE(X,m,n))
 /* GETARG macros for varlena types will typically look like this: */
 #define PG_GETARG_BYTEA_P(n)           DatumGetByteaP(PG_GETARG_DATUM(n))
 #define PG_GETARG_TEXT_P(n)                    DatumGetTextP(PG_GETARG_DATUM(n))
@@ -197,6 +207,11 @@ extern struct varlena *pg_detoast_datum_copy(struct varlena * datum);
 #define PG_GETARG_TEXT_P_COPY(n)       DatumGetTextPCopy(PG_GETARG_DATUM(n))
 #define PG_GETARG_BPCHAR_P_COPY(n)     DatumGetBpCharPCopy(PG_GETARG_DATUM(n))
 #define PG_GETARG_VARCHAR_P_COPY(n) DatumGetVarCharPCopy(PG_GETARG_DATUM(n))
+/* And a b-byte slice from position a -also OK to write */
+#define PG_GETARG_BYTEA_P_SLICE(n,a,b) DatumGetByteaPSlice(PG_GETARG_DATUM(n),a,b)
+#define PG_GETARG_TEXT_P_SLICE(n,a,b)  DatumGetTextPSlice(PG_GETARG_DATUM(n),a,b)
+#define PG_GETARG_BPCHAR_P_SLICE(n,a,b) DatumGetBpCharPSlice(PG_GETARG_DATUM(n),a,b)
+#define PG_GETARG_VARCHAR_P_SLICE(n,a,b) DatumGetVarCharPSlice(PG_GETARG_DATUM(n),a,b)
 
 /* To return a NULL do this: */
 #define PG_RETURN_NULL()  \
index a349b74f35f5feb001590fdc8e80a909ad442239..c8811dc17da5a33eb7e424b5b5ee54a135b8d4a2 100644 (file)
@@ -123,6 +123,7 @@ typedef struct AlterTableStmt
                                                                 *      A = add column
                                                                 *      T = alter column default
                                                                 *      S = alter column statistics
+                                                                *  M = alter column storage
                                                                 *      D = drop column
                                                                 *      C = add constraint
                                                                 *      X = drop constraint