diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 6bc5ac5445..2d87902b2f 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -1,5 +1,5 @@
@@ -30,6 +30,8 @@ ALTER TABLE [ ONLY ] table [ * ]
class="PARAMETER">value | DROP DEFAULT }
ALTER TABLE [ ONLY ] table [ * ]
ALTER [ COLUMN ] column SET STATISTICS integer
+ALTER TABLE [ ONLY ] table [ * ]
+ ALTER [ COLUMN ] column SET STORAGE {PLAIN | EXTERNAL | EXTENDED | MAIN}
ALTER TABLE [ ONLY ] table [ * ]
RENAME [ COLUMN ] column TO newcolumn
@@ -169,6 +171,17 @@ ALTER TABLE table
The ALTER COLUMN SET STATISTICS form allows you to
set the statistics-gathering target for subsequent
operations.
+ The ALTER COLUMN SET STORAGE 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. PLAIN must be used
+ for fixed-length values such as INTEGER and is
+ inline, uncompressed. MAIN is for inline,
+ compressible data. EXTERNAL is for external,
+ uncompressed data and EXTENDED is for external,
+ compressed data. The use of EXTERNAL will make
+ substring operations on a column faster, at the penalty of
+ increased storage space.
The RENAME 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
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 20341077c9..94c664cbce 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1,5 +1,5 @@
@@ -1296,6 +1296,35 @@ concat_text(PG_FUNCTION_ARGS)
this works in both strict and nonstrict functions.
+
+ Other options provided in the new-style interface are two
+ variants of the
+ PG_GETARG_xxx()
+ macros. The first of these,
+ PG_GETARG_xxx_COPY()
+ 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
+ PG_GETARG_xxx_COPY()
+ macros guarantees a writable result.)
+
+
+
+ The second variant consists of the
+ PG_GETARG_xxx_SLICE()
+ 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
+ ALTER TABLE tablename ALTER
+ COLUMN colname SET STORAGE
+ storagetype. Storage type is one of
+ plain, external, extended or main.)
+
+
The version-1 function call conventions make it possible to
return set
results and implement trigger functions and
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 48a15cf5d3..2a0b5c2762 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.27 2002/01/16 20:29:01 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.28 2002/03/05 05:33:06 momjian Exp $
*
*
* INTERFACE ROUTINES
@@ -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 */
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index eefbe269f3..e49c8ca321 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.157 2002/03/02 21:39:22 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.158 2002/03/05 05:33:08 momjian Exp $
*
* NOTES
* The PerformAddAttribute() code, like most of the relation
@@ -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 */
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9ff44e9d93..dfc8898653 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.283 2002/03/02 21:39:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.284 2002/03/05 05:33:14 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -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 ALTER [COLUMN] SET STORAGE */
+ | 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 DROP [COLUMN] {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"; }
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 12b05c6bcd..eaa0f7fa2b 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.100 2002/02/18 23:11:18 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.101 2002/03/05 05:33:15 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -243,6 +243,7 @@ static ScanKeyword ScanKeywords[] = {
{"statistics", STATISTICS},
{"stdin", STDIN},
{"stdout", STDOUT},
+ {"storage", STORAGE},
{"substring", SUBSTRING},
{"sysid", SYSID},
{"table", TABLE},
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4e2b89508e..528b93012c 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.128 2002/03/01 22:45:14 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.129 2002/03/05 05:33:19 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -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,
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 7e55bfa4ba..b10ec7d685 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.78 2001/11/19 19:15:07 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.79 2002/03/05 05:33:19 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -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));
}
/*
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 6fffd7bab8..64988a2077 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.57 2001/11/05 17:46:30 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.58 2002/03/05 05:33:20 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -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);
+}
diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h
index 7bc55dc53a..95a1fe8e00 100644
--- a/src/include/access/tuptoaster.h
+++ b/src/include/access/tuptoaster.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 2000, PostgreSQL Development Team
*
- * $Id: tuptoaster.h,v 1.13 2001/11/05 17:46:31 momjian Exp $
+ * $Id: tuptoaster.h,v 1.14 2002/03/05 05:33:25 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -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 -
*
diff --git a/src/include/commands/command.h b/src/include/commands/command.h
index ee4e2c0aa3..cf09111af4 100644
--- a/src/include/commands/command.h
+++ b/src/include/commands/command.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: command.h,v 1.32 2002/02/26 22:47:10 tgl Exp $
+ * $Id: command.h,v 1.33 2002/03/05 05:33:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -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,
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 51adf1c5cd..017f73fb75 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -11,7 +11,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: fmgr.h,v 1.18 2001/11/05 17:46:31 momjian Exp $
+ * $Id: fmgr.h,v 1.19 2002/03/05 05:33:22 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -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() \
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 564985ae0e..c6b1feb79b 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parsenodes.h,v 1.155 2002/03/01 22:45:18 petere Exp $
+ * $Id: parsenodes.h,v 1.156 2002/03/05 05:33:31 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -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