Index: doc/src/sgml/xfunc.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v
retrieving revision 1.38
diff -c -r1.38 xfunc.sgml
*** doc/src/sgml/xfunc.sgml 2001/09/16 16:11:09 1.38
--- doc/src/sgml/xfunc.sgml 2001/10/15 22:50:02
***************
*** 1137,1142 ****
--- 1137,1171 ----
+ 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
procedural-language call handlers. Version-1 code is also more
Index: doc/src/sgml/ref/alter_table.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v
retrieving revision 1.29
diff -c -r1.29 alter_table.sgml
*** doc/src/sgml/ref/alter_table.sgml 2001/10/12 00:07:14 1.29
--- doc/src/sgml/ref/alter_table.sgml 2001/10/15 22:50:02
***************
*** 31,36 ****
--- 31,38 ----
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
ALTER TABLE table
***************
*** 170,175 ****
--- 172,187 ----
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 and slicing operations on a column faster.
The RENAME clause causes the name of a table or column
to change without changing any of the data contained in
the affected table. Thus, the table or column will
Index: src/backend/access/heap/tuptoaster.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v
retrieving revision 1.24
diff -c -r1.24 tuptoaster.c
*** src/backend/access/heap/tuptoaster.c 2001/08/10 18:57:33 1.24
--- src/backend/access/heap/tuptoaster.c 2001/10/15 22:50:04
***************
*** 47,54 ****
--- 47,57 ----
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);
+
/* ----------
* heap_tuple_toast_attrs -
*
***************
*** 165,171 ****
--- 168,248 ----
return result;
}
+ /* ----------
+ * 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 -
*
***************
*** 1097,1100 ****
--- 1174,1385 ----
}
+ /* ----------
+ * 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;
+ int numchunks;
+ int startchunk;
+ int endchunk;
+ int32 startoffset;
+ int32 endoffset;
+ int totalchunks;
+ Pointer chunk;
+ bool isnull;
+ int32 chunksize;
+ int32 chcpystrt;
+ int32 chcpyend;
+
+ char *chunks_found;
+ char *chunks_expected;
+
+ 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;
+
+ chunks_found = palloc(numchunks);
+ chunks_expected = palloc(numchunks);
+ memset(chunks_found, 0, numchunks);
+ memset(chunks_expected, 1, numchunks);
+
+ /*
+ * 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
+ *
+ * Note we will not necessarily see the chunks in sequence-number order.
+ */
+ toastscan = index_beginscan(toastidx, false, nscankeys, &toastkey[0]);
+ while ((indexRes = index_getnext(toastscan, ForwardScanDirection)) != NULL)
+ {
+ toasttup.t_self = indexRes->heap_iptr;
+ heap_fetch(toastrel, SnapshotAny, &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 < startchunk || residx > endchunk)
+ elog(ERROR, "unexpected chunk number %d for toast value %u",
+ residx,
+ 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);
+ }
+ if (chunks_found[residx - startchunk]++ > 0)
+ elog(ERROR, "chunk %d for toast value %u appears multiple times",
+ 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);
+ }
+
+ /*
+ * Final checks that we successfully fetched the datum
+ */
+ if (memcmp(chunks_found, chunks_expected, numchunks) != 0)
+ elog(ERROR, "not all toast chunks found for value %u",
+ attr->va_content.va_external.va_valueid);
+ pfree(chunks_expected);
+ pfree(chunks_found);
+
+ /*
+ * End scan and close relations
+ */
+ index_endscan(toastscan);
+ index_close(toastidx);
+ heap_close(toastrel, AccessShareLock);
+
+ return result;
+ }
+
+
#endif /* TUPLE_TOASTER_ACTIVE */
+
+
+
Index: src/backend/commands/command.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/command.c,v
retrieving revision 1.144
diff -c -r1.144 command.c
*** src/backend/commands/command.c 2001/10/12 00:07:14 1.144
--- src/backend/commands/command.c 2001/10/15 22:50:06
***************
*** 56,63 ****
static void drop_default(Oid relid, int16 attnum);
static bool needs_toast_table(Relation rel);
static bool is_relation(char *name);
-
/* --------------------------------
* PortalCleanup
* --------------------------------
--- 56,63 ----
static void drop_default(Oid relid, int16 attnum);
static bool needs_toast_table(Relation rel);
static bool is_relation(char *name);
+ static Oid AlterTableColumnSetup(const char *relationName, int system_ok);
/* --------------------------------
* PortalCleanup
* --------------------------------
***************
*** 262,268 ****
--- 262,348 ----
PortalDrop(portal);
}
+ /*
+ * ALTER TABLE ALTER|ADD COLUMN support routines
+ *
+ */
+
+ /* ----------------
+ * AlterTableColumnSetup
+ * 1. Checks that the operation is OK for the specified column.
+ * (not a system table*, owner ok, is a table)
+ * 2. Get an exclusive lock on the Relation
+ * 3. Returns OID of relation
+ *
+ * *if system_ok == 1, this check is skipped.
+ * (statistics can be changed on system tables)
+ * ----------------
+ */
+
+ Oid
+ AlterTableColumnSetup(const char *relationName, int system_ok)
+ {
+ Relation rel;
+ Oid myrelid;
+
+ /*
+ * permissions checking. this would normally be done in utility.c,
+ * but the ALTER COLUMN etc. routines are recursive.
+ *
+ * normally, only the owner of a class can change its schema.
+ */
+ if (!allowSystemTableMods && IsSystemRelationName(relationName)
+ && !system_ok)
+ elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+ relationName);
+
+ if (!pg_ownercheck(GetUserId(), relationName, RELNAME))
+ elog(ERROR, "ALTER TABLE: permission denied");
+
+ /*
+ * Grab an exclusive lock on the target table, which we will NOT
+ * release until end of transaction.
+ */
+ rel = heap_openr(relationName, AccessExclusiveLock);
+
+ if (rel->rd_rel->relkind != RELKIND_RELATION)
+ elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
+ relationName);
+
+ myrelid = RelationGetRelid(rel);
+ heap_close(rel, NoLock); /* close rel but keep lock! */
+ return myrelid;
+ }
+
/* ----------------
+ * RecurseOverChildren, RecurseOverChildrenEnd
+ * Support macros to bracket code to be executed over all the children
+ * of relation relid. childrelname is set successively to each child name.
+ * ----------------
+ */
+
+ #define RecurseOverChildren(relid) \
+ { \
+ List *child, \
+ *children; \
+ Relation rel; \
+ children = find_all_inheritors( relid ); \
+ foreach(child, children) \
+ { \
+ Oid childrelid = lfirsti(child); \
+ char *childrelname; \
+ \
+ if (childrelid == relid ) \
+ continue; \
+ rel = heap_open(childrelid, AccessExclusiveLock); \
+ childrelname = pstrdup(RelationGetRelationName(rel)); \
+ heap_close(rel, AccessExclusiveLock);
+
+ #define RecurseOverChildrenEnd pfree(childrelname); } }
+
+
+
+ /* ----------------
* AlterTableAddColumn
* (formerly known as PerformAddAttribute)
*
***************
*** 314,343 ****
char *typename;
int attndims;
! /*
! * permissions checking. this would normally be done in utility.c,
! * but this particular routine is recursive.
! *
! * normally, only the owner of a class can change its schema.
! */
! if (!allowSystemTableMods && IsSystemRelationName(relationName))
! elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
! relationName);
! if (!pg_ownercheck(GetUserId(), relationName, RELNAME))
! elog(ERROR, "ALTER TABLE: permission denied");
/*
! * Grab an exclusive lock on the target table, which we will NOT
! * release until end of transaction.
*/
! rel = heap_openr(relationName, AccessExclusiveLock);
! if (rel->rd_rel->relkind != RELKIND_RELATION)
! elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
! relationName);
- myrelid = RelationGetRelid(rel);
- heap_close(rel, NoLock); /* close rel but keep lock! */
/*
* Recurse to add the column to child classes, if requested.
--- 394,412 ----
char *typename;
int attndims;
! /* Standard setup */
!
! myrelid = AlterTableColumnSetup(relationName,0);
/*
! * we can't add a not null attribute
*/
! if (colDef->is_not_null)
! elog(ERROR, "Can't add a NOT NULL attribute to an existing relation");
! if (colDef->raw_default || colDef->cooked_default)
! elog(ERROR, "Adding columns with defaults is not implemented.");
/*
* Recurse to add the column to child classes, if requested.
***************
*** 348,379 ****
*/
if (inherits)
{
! List *child,
! *children;
!
! /* this routine is actually in the planner */
! children = find_all_inheritors(myrelid);
!
! /*
! * find_all_inheritors does the recursive search of the
! * inheritance hierarchy, so all we have to do is process all
! * of the relids in the list that it returns.
! */
! foreach(child, children)
! {
! Oid childrelid = lfirsti(child);
! char *childrelname;
!
! if (childrelid == myrelid)
! continue;
! rel = heap_open(childrelid, AccessExclusiveLock);
! childrelname = pstrdup(RelationGetRelationName(rel));
! heap_close(rel, AccessExclusiveLock);
!
! AlterTableAddColumn(childrelname, false, colDef);
!
! pfree(childrelname);
! }
}
/*
--- 417,425 ----
*/
if (inherits)
{
! RecurseOverChildren(myrelid); /* macro */
! AlterTableAddColumn(childrelname, false, colDef);
! RecurseOverChildrenEnd ; /* macro */
}
/*
***************
*** 397,403 ****
elog(ERROR, "Adding NOT NULL columns is not implemented."
"\n\tAdd the column, then use ALTER TABLE ADD CONSTRAINT.");
-
rel = heap_openr(RelationRelationName, RowExclusiveLock);
reltup = SearchSysCache(RELNAME,
--- 443,448 ----
***************
*** 543,590 ****
int16 attnum;
Oid myrelid;
! if (!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");
! #endif
!
! rel = heap_openr(relationName, AccessExclusiveLock);
! if (rel->rd_rel->relkind != RELKIND_RELATION)
! elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
! relationName);
! myrelid = RelationGetRelid(rel);
! heap_close(rel, NoLock);
/*
* Propagate to children if desired
*/
if (inh)
{
! List *child,
! *children;
!
! /* this routine is actually in the planner */
! children = find_all_inheritors(myrelid);
!
! /*
! * find_all_inheritors does the recursive search of the
! * inheritance hierarchy, so all we have to do is process all of
! * the relids in the list that it returns.
! */
! foreach(child, children)
! {
! Oid childrelid = lfirsti(child);
!
! if (childrelid == myrelid)
! continue;
! rel = heap_open(childrelid, AccessExclusiveLock);
! AlterTableAlterColumnDefault(RelationGetRelationName(rel),
! false, colName, newDefault);
! heap_close(rel, AccessExclusiveLock);
! }
}
/* -= now do the thing on this relation =- */
--- 588,603 ----
int16 attnum;
Oid myrelid;
! myrelid = AlterTableColumnSetup(relationName, 0);
/*
* Propagate to children if desired
*/
if (inh)
{
! RecurseOverChildren(myrelid) ;
! AlterTableAlterColumnDefault(childrelname, false, colName, newDefault);
! RecurseOverChildrenEnd ;
}
/* -= now do the thing on this relation =- */
***************
*** 700,770 ****
/*
! * ALTER TABLE ALTER COLUMN SET STATISTICS
*/
void
! AlterTableAlterColumnStatistics(const char *relationName,
! bool inh, const char *colName,
! Node *statsTarget)
{
- Relation rel;
Oid myrelid;
! int newtarget;
Relation attrelation;
HeapTuple tuple;
! #ifndef NO_SECURITY
! if (!pg_ownercheck(GetUserId(), relationName, RELNAME))
! elog(ERROR, "ALTER TABLE: permission denied");
! #endif
! rel = heap_openr(relationName, AccessExclusiveLock);
! if (rel->rd_rel->relkind != RELKIND_RELATION)
! elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
! relationName);
! myrelid = RelationGetRelid(rel);
! heap_close(rel, NoLock); /* close rel, but keep lock! */
/*
* Propagate to children if desired
*/
if (inh)
{
! List *child,
! *children;
!
! /* this routine is actually in the planner */
! children = find_all_inheritors(myrelid);
!
! /*
! * find_all_inheritors does the recursive search of the
! * inheritance hierarchy, so all we have to do is process all of
! * the relids in the list that it returns.
! */
! foreach(child, children)
! {
! Oid childrelid = lfirsti(child);
!
! if (childrelid == myrelid)
! continue;
! rel = heap_open(childrelid, AccessExclusiveLock);
! AlterTableAlterColumnStatistics(RelationGetRelationName(rel),
! false, colName, statsTarget);
! 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,
--- 713,783 ----
/*
! * ALTER TABLE ALTER COLUMN SET STATISTICS / STORAGE
*/
void
! AlterTableAlterColumnFlags(const char *relationName,
! bool inh, const char *colName,
! Node *flagValue,
! const char *flagType)
{
Oid myrelid;
! int newtarget = 0;
! char *storagemode;
! char newstorage = 'x';
Relation attrelation;
HeapTuple tuple;
! /* Allow statistics changes on system tables */
! myrelid = AlterTableColumnSetup(relationName, (*flagType == 'S'));
! /* 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, "AlterTable: \"%s\" storage not recognized",
! storagemode);
! }
! else
! {
! elog(ERROR,"ALTER TABLE: Invalid column flag: %c", (int) *flagType);
! }
/*
* Propagate to children if desired
*/
if (inh)
{
! RecurseOverChildren(myrelid);
! AlterTableAlterColumnFlags(childrelname,
! false, colName, flagValue,
! flagType);
! RecurseOverChildrenEnd;
}
/* -= now do the thing on this relation =- */
attrelation = heap_openr(AttributeRelationName, RowExclusiveLock);
tuple = SearchSysCacheCopy(ATTNAME,
***************
*** 779,785 ****
elog(ERROR, "ALTER TABLE: cannot change system attribute \"%s\"",
colName);
! ((Form_pg_attribute) GETSTRUCT(tuple))->attstattarget = newtarget;
simple_heap_update(attrelation, &tuple->t_self, tuple);
--- 792,814 ----
elog(ERROR, "ALTER TABLE: cannot change system attribute \"%s\"",
colName);
! /* 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);
***************
*** 1040,1072 ****
if (inh)
elog(ERROR, "ALTER TABLE / DROP COLUMN with inherit option is not supported yet");
! /*
! * permissions checking. this would normally be done in utility.c,
! * but this particular routine is recursive.
! *
! * normally, only the owner of a class can change its schema.
! */
! if (!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");
! #endif
!
! /*
! * Grab an exclusive lock on the target table, which we will NOT
! * release until end of transaction.
! */
! rel = heap_openr(relationName, AccessExclusiveLock);
!
! if (rel->rd_rel->relkind != RELKIND_RELATION)
! elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
! relationName);
- myrelid = RelationGetRelid(rel);
- heap_close(rel, NoLock); /* close rel but keep lock! */
-
/*
* What to do when rel has inheritors ?
*/
--- 1069,1076 ----
if (inh)
elog(ERROR, "ALTER TABLE / DROP COLUMN with inherit option is not supported yet");
! myrelid = AlterTableColumnSetup(relationName,0);
/*
* What to do when rel has inheritors ?
*/
***************
*** 1336,1344 ****
* the constraint against tuples already in
* the table.
*/
AddRelationRawConstraints(rel, NIL,
makeList1(constr));
!
break;
}
case CONSTR_UNIQUE:
--- 1340,1349 ----
* the constraint against tuples already in
* the table.
*/
+
AddRelationRawConstraints(rel, NIL,
makeList1(constr));
!
break;
}
case CONSTR_UNIQUE:
Index: src/backend/parser/gram.y
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.262
diff -c -r2.262 gram.y
*** src/backend/parser/gram.y 2001/10/10 00:02:42 2.262
--- src/backend/parser/gram.y 2001/10/15 22:50:12
***************
*** 353,359 ****
OFFSET, OIDS, OPERATOR, OWNER, PASSWORD, PROCEDURAL,
REINDEX, RENAME, RESET, RETURNS, ROW, RULE,
SEQUENCE, SETOF, SHARE, SHOW, START, STATEMENT,
! STATISTICS, STDIN, STDOUT, SYSID,
TEMP, TEMPLATE, TOAST, TRUNCATE, TRUSTED,
UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
--- 353,359 ----
OFFSET, OIDS, OPERATOR, OWNER, PASSWORD, PROCEDURAL,
REINDEX, RENAME, RESET, RETURNS, ROW, RULE,
SEQUENCE, SETOF, SHARE, SHOW, START, STATEMENT,
! STATISTICS, STDIN, STDOUT, STORAGE, SYSID,
TEMP, TEMPLATE, TOAST, TRUNCATE, TRUSTED,
UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
***************
*** 1027,1032 ****
--- 1027,1043 ----
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
{
***************
*** 5732,5737 ****
--- 5743,5749 ----
| STATISTICS { $$ = "statistics"; }
| STDIN { $$ = "stdin"; }
| STDOUT { $$ = "stdout"; }
+ | STORAGE { $$ = "storage"; }
| SYSID { $$ = "sysid"; }
| TEMP { $$ = "temp"; }
| TEMPLATE { $$ = "template"; }
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/keywords.c,v
retrieving revision 1.99
diff -c -r1.99 keywords.c
*** src/backend/parser/keywords.c 2001/10/10 00:02:42 1.99
--- src/backend/parser/keywords.c 2001/10/15 22:50:12
***************
*** 243,248 ****
--- 243,249 ----
{"statistics", STATISTICS},
{"stdin", STDIN},
{"stdout", STDOUT},
+ {"storage", STORAGE},
{"substring", SUBSTRING},
{"sysid", SYSID},
{"table", TABLE},
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.120
diff -c -r1.120 utility.c
*** src/backend/tcop/utility.c 2001/10/12 00:07:14 1.120
--- src/backend/tcop/utility.c 2001/10/15 22:50:14
***************
*** 435,445 ****
stmt->name,
stmt->def);
break;
! case 'S': /* ALTER COLUMN STATISTICS */
! AlterTableAlterColumnStatistics(stmt->relname,
interpretInhOption(stmt->inhOpt),
! stmt->name,
! stmt->def);
break;
case 'D': /* DROP COLUMN */
AlterTableDropColumn(stmt->relname,
--- 435,446 ----
stmt->name,
stmt->def);
break;
! case 'S': case 'M': /* ALTER COLUMN STATISTICS/STORAGE */
! AlterTableAlterColumnFlags(stmt->relname,
interpretInhOption(stmt->inhOpt),
! stmt->name,
! stmt->def,
! &(stmt->subtype));
break;
case 'D': /* DROP COLUMN */
AlterTableDropColumn(stmt->relname,
Index: src/backend/utils/adt/varlena.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/adt/varlena.c,v
retrieving revision 1.73
diff -c -r1.73 varlena.c
*** src/backend/utils/adt/varlena.c 2001/09/14 17:46:40 1.73
--- src/backend/utils/adt/varlena.c 2001/10/15 22:50:15
***************
*** 355,404 ****
* 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
*/
Datum
text_substr(PG_FUNCTION_ARGS)
{
! text *string = PG_GETARG_TEXT_P(0);
int32 m = PG_GETARG_INT32(1);
int32 n = PG_GETARG_INT32(2);
! text *ret;
! int len;
!
#ifdef MULTIBYTE
int i;
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;
}
! /*
! * starting position before the start of the string? then offset into
! * the string per SQL92 spec...
! */
! else if (m < 1)
{
! n += (m - 1);
m = 1;
}
/* m will now become a zero-based starting position */
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);
--- 355,434 ----
* 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 2001-10-10
*/
Datum
text_substr(PG_FUNCTION_ARGS)
{
! text *string;
int32 m = PG_GETARG_INT32(1);
int32 n = PG_GETARG_INT32(2);
! int eml = 1;
! int32 sm;
! int32 sn;
#ifdef MULTIBYTE
int i;
char *p;
+ text *ret;
+ int len;
+ #endif MULTIBYTE
! /*
! * starting position before the start of the string? then offset into
! * the string per SQL92 spec...
! */
!
! if (m < 1)
! {
! n += (m - 1);
! m = 1;
! }
! /* Checks for m > octet length are made by toast routines */
! sm = m - 1; /* m is 1-based, access routine is 0-based */
! sn = n;
!
#ifdef MULTIBYTE
! eml = pg_database_encoding_max_length();
!
! if (eml > 1)
{
! sm = 0;
! sn = (m + n)*eml + 3;
! /* +3 to avoid problems with mb chars which overhang the slice end*/
}
+ #endif
! string = PG_GETARG_TEXT_P_SLICE(0,sm,sn);
!
! if (eml == 1) /* Single byte representation */
{
! PG_RETURN_TEXT_P(string);
! }
!
! #ifndef MULTIBYTE
! PG_RETURN_NULL(); /* notreached: just to remove compiler warning */
! #endif
!
! /* Remainder only used in multibyte ("true multibyte") case. */
!
! #ifdef MULTIBYTE
!
! len = pg_mbstrlen_with_len(VARDATA(string), sn - 3);
!
! if (m > len) {
m = 1;
+ n = 0;
}
/* m will now become a zero-based starting position */
+
m--;
if (((m + n) > len) || (n < 0))
n = (len - m);
p = VARDATA(string);
for (i = 0; i < m; i++)
p += pg_mblen(p);
***************
*** 406,412 ****
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;
--- 436,441 ----
***************
*** 414,419 ****
--- 443,450 ----
memcpy(VARDATA(ret), VARDATA(string) + m, n);
PG_RETURN_TEXT_P(ret);
+ #endif
+
}
/*
***************
*** 765,790 ****
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)
{
n += (m - 1);
m = 1;
--- 796,809 ----
Datum
bytea_substr(PG_FUNCTION_ARGS)
{
int32 m = PG_GETARG_INT32(1);
int32 n = PG_GETARG_INT32(2);
/*
* starting position before the start of the string? then offset into
* the string per SQL92 spec...
*/
! if (m < 1)
{
n += (m - 1);
m = 1;
***************
*** 792,806 ****
/* 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);
}
/*
--- 811,818 ----
/* m will now become a zero-based starting position */
m--;
! PG_RETURN_BYTEA_P(PG_GETARG_BYTEA_P_SLICE(0,m,n));
}
/*
***************
*** 863,869 ****
len = VARSIZE(v) - VARHDRSZ;
! if (n < 0 || n >= len)
elog(ERROR, "byteaGetByte: index %d out of range [0..%d]",
n, len - 1);
--- 875,881 ----
len = VARSIZE(v) - VARHDRSZ;
! if (n < 0 || n > len)
elog(ERROR, "byteaGetByte: index %d out of range [0..%d]",
n, len - 1);
***************
*** 1218,1220 ****
--- 1230,1235 ----
PG_RETURN_INT32(cmp);
}
+
+
+
Index: src/backend/utils/fmgr/fmgr.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v
retrieving revision 1.55
diff -c -r1.55 fmgr.c
*** src/backend/utils/fmgr/fmgr.c 2001/10/06 23:21:44 1.55
--- src/backend/utils/fmgr/fmgr.c 2001/10/15 22:50:16
***************
*** 1519,1521 ****
--- 1519,1528 ----
return result;
}
}
+
+ struct varlena *
+ pg_detoast_datum_slice(struct varlena * datum, int32 first, int32 count)
+ {
+ /* Only get the specified proportion from the toast rel */
+ return (struct varlena *) heap_tuple_untoast_attr_slice((varattrib *) datum, first, count);
+ }
Index: src/include/fmgr.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/fmgr.h,v
retrieving revision 1.15
diff -c -r1.15 fmgr.h
*** src/include/fmgr.h 2001/10/06 23:21:44 1.15
--- src/include/fmgr.h 2001/10/15 22:50:18
***************
*** 135,145 ****
--- 135,150 ----
*/
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,192 ****
--- 192,202 ----
#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,202 ****
--- 207,217 ----
#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))
+ /* Variants which return a b-byte slice from a(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: src/include/access/tuptoaster.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/access/tuptoaster.h,v
retrieving revision 1.11
diff -c -r1.11 tuptoaster.h
*** src/include/access/tuptoaster.h 2001/05/07 00:43:24 1.11
--- src/include/access/tuptoaster.h 2001/10/15 22:50:18
***************
*** 100,105 ****
--- 100,116 ----
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 -
*
* Create a compressed version of a varlena datum, if possible
Index: src/include/commands/command.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/commands/command.h,v
retrieving revision 1.28
diff -c -r1.28 command.h
*** src/include/commands/command.h 2001/10/12 00:07:15 1.28
--- src/include/commands/command.h 2001/10/15 22:50:18
***************
*** 47,55 ****
bool inh, const char *colName,
Node *newDefault);
! extern void AlterTableAlterColumnStatistics(const char *relationName,
! bool inh, const char *colName,
! Node *statsTarget);
extern void AlterTableDropColumn(const char *relationName,
bool inh, const char *colName,
--- 47,56 ----
bool inh, const char *colName,
Node *newDefault);
! extern void AlterTableAlterColumnFlags(const char *relationName,
! bool inh, const char *colName,
! Node *flagValue,
! const char *flagType);
extern void AlterTableDropColumn(const char *relationName,
bool inh, const char *colName,
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.146
diff -c -r1.146 parsenodes.h
*** src/include/nodes/parsenodes.h 2001/10/12 00:07:15 1.146
--- src/include/nodes/parsenodes.h 2001/10/15 22:50:20
***************
*** 121,126 ****
--- 121,127 ----
* A = add column
* T = alter column default
* S = alter column statistics
+ * M = alter column storage (TOAST) mode
* D = drop column
* C = add constraint
* X = drop constraint