I attach a version of my toast-slicing patch, against current CVS

(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
This commit is contained in:
Bruce Momjian 2002-03-05 05:33:31 +00:00
parent 276fc7ce82
commit 03194432de
13 changed files with 498 additions and 79 deletions

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.38 2002/02/17 13:29:00 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.39 2002/03/05 05:33:04 momjian Exp $
PostgreSQL documentation
-->
@ -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

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.47 2002/01/20 22:19:56 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.48 2002/03/05 05:33:00 momjian Exp $
-->
<chapter id="xfunc">
@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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 <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"; }

View File

@ -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},

View File

@ -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,

View File

@ -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));
}
/*

View File

@ -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);
}

View File

@ -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 -
*

View File

@ -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,

View File

@ -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() \

View File

@ -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