diff options
Diffstat (limited to 'src/backend')
37 files changed, 905 insertions, 946 deletions
diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile index 7cd4a4ab0e..103bae2f5b 100644 --- a/src/backend/access/common/Makefile +++ b/src/backend/access/common/Makefile @@ -4,7 +4,7 @@ # Makefile for access/common # # IDENTIFICATION -# $PostgreSQL: pgsql/src/backend/access/common/Makefile,v 1.21 2006/01/14 22:03:35 tgl Exp $ +# $PostgreSQL: pgsql/src/backend/access/common/Makefile,v 1.22 2006/07/03 22:45:36 tgl Exp $ # #------------------------------------------------------------------------- @@ -12,7 +12,7 @@ subdir = src/backend/access/common top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = heaptuple.o indextuple.o printtup.o scankey.o tupdesc.o +OBJS = heaptuple.o indextuple.o printtup.o reloptions.o scankey.o tupdesc.o all: SUBSYS.o diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index edd90c8a56..70e8fd948d 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -16,7 +16,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.108 2006/07/02 02:23:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.109 2006/07/03 22:45:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1694,9 +1694,8 @@ minimal_tuple_from_heap_tuple(HeapTuple htup) * presumed to contain no null fields and no varlena fields. * * This routine is really only useful for certain system tables that are - * known to be fixed-width and null-free. It is used in some places for - * pg_class, but that is a gross hack (it only works because relacl can - * be omitted from the tuple entirely in those places). + * known to be fixed-width and null-free. Currently it is only used for + * pg_attribute tuples. * ---------------- */ HeapTuple @@ -1738,54 +1737,3 @@ heap_addheader(int natts, /* max domain index */ return tuple; } - -/* - * build_class_tuple - * - * XXX Natts_pg_class_fixed is a hack - see pg_class.h - */ -HeapTuple -build_class_tuple(Form_pg_class pgclass, ArrayType *options) -{ - HeapTuple tuple; - HeapTupleHeader td; - Form_pg_class data; /* contents of tuple */ - Size len; - Size size; - int hoff; - - /* size of pg_class tuple with options */ - if (options) - size = offsetof(FormData_pg_class, reloptions) + VARATT_SIZE(options); - else - size = CLASS_TUPLE_SIZE; - - /* header needs no null bitmap */ - hoff = offsetof(HeapTupleHeaderData, t_bits); - hoff += sizeof(Oid); - hoff = MAXALIGN(hoff); - len = hoff + size; - - tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len); - tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE); - - tuple->t_len = len; - ItemPointerSetInvalid(&(tuple->t_self)); - tuple->t_tableOid = InvalidOid; - - /* we don't bother to fill the Datum fields */ - - td->t_natts = Natts_pg_class_fixed; - td->t_hoff = hoff; - td->t_infomask = HEAP_HASOID; - - data = (Form_pg_class) ((char *) td + hoff); - memcpy(data, pgclass, CLASS_TUPLE_SIZE); - if (options) - { - td->t_natts++; - memcpy(data->reloptions, options, VARATT_SIZE(options)); - } - - return tuple; -} diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c new file mode 100644 index 0000000000..8506070f10 --- /dev/null +++ b/src/backend/access/common/reloptions.c @@ -0,0 +1,326 @@ +/*------------------------------------------------------------------------- + * + * reloptions.c + * Core support for relation options (pg_class.reloptions) + * + * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.1 2006/07/03 22:45:36 tgl Exp $ + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/reloptions.h" +#include "catalog/pg_type.h" +#include "commands/defrem.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/rel.h" + + +/* + * Transform a relation options list (list of DefElem) into the text array + * format that is kept in pg_class.reloptions. + * + * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and + * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing + * reloptions value (possibly NULL), and we replace or remove entries + * as needed. + * + * If ignoreOids is true, then we should ignore any occurrence of "oids" + * in the list (it will be or has been handled by interpretOidsOption()). + * + * Note that this is not responsible for determining whether the options + * are valid. + * + * Both oldOptions and the result are text arrays (or NULL for "default"), + * but we declare them as Datums to avoid including array.h in reloptions.h. + */ +Datum +transformRelOptions(Datum oldOptions, List *defList, + bool ignoreOids, bool isReset) +{ + Datum result; + ArrayBuildState *astate; + ListCell *cell; + + /* no change if empty list */ + if (defList == NIL) + return oldOptions; + + /* We build new array using accumArrayResult */ + astate = NULL; + + /* Copy any oldOptions that aren't to be replaced */ + if (oldOptions != (Datum) 0) + { + ArrayType *array = DatumGetArrayTypeP(oldOptions); + Datum *oldoptions; + int noldoptions; + int i; + + Assert(ARR_ELEMTYPE(array) == TEXTOID); + + deconstruct_array(array, TEXTOID, -1, false, 'i', + &oldoptions, NULL, &noldoptions); + + for (i = 0; i < noldoptions; i++) + { + text *oldoption = DatumGetTextP(oldoptions[i]); + char *text_str = (char *) VARATT_DATA(oldoption); + int text_len = VARATT_SIZE(oldoption) - VARHDRSZ; + + /* Search for a match in defList */ + foreach(cell, defList) + { + DefElem *def = lfirst(cell); + int kw_len = strlen(def->defname); + + if (text_len > kw_len && text_str[kw_len] == '=' && + pg_strncasecmp(text_str, def->defname, kw_len) == 0) + break; + } + if (!cell) + { + /* No match, so keep old option */ + astate = accumArrayResult(astate, oldoptions[i], + false, TEXTOID, + CurrentMemoryContext); + } + } + } + + /* + * If CREATE/SET, add new options to array; if RESET, just check that + * the user didn't say RESET (option=val). (Must do this because the + * grammar doesn't enforce it.) + */ + foreach(cell, defList) + { + DefElem *def = lfirst(cell); + + if (isReset) + { + if (def->arg != NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("RESET must not include values for parameters"))); + } + else + { + text *t; + const char *value; + Size len; + + if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0) + continue; + + /* + * Flatten the DefElem into a text string like "name=arg". + * If we have just "name", assume "name=true" is meant. + */ + if (def->arg != NULL) + value = defGetString(def); + else + value = "true"; + len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value); + /* +1 leaves room for sprintf's trailing null */ + t = (text *) palloc(len + 1); + VARATT_SIZEP(t) = len; + sprintf((char *) VARATT_DATA(t), "%s=%s", def->defname, value); + + astate = accumArrayResult(astate, PointerGetDatum(t), + false, TEXTOID, + CurrentMemoryContext); + } + } + + if (astate) + result = makeArrayResult(astate, CurrentMemoryContext); + else + result = (Datum) 0; + + return result; +} + + +/* + * Interpret reloptions that are given in text-array format. + * + * options: array of "keyword=value" strings, as built by transformRelOptions + * numkeywords: number of legal keywords + * keywords: the allowed keywords + * values: output area + * validate: if true, throw error for unrecognized keywords. + * + * The keywords and values arrays must both be of length numkeywords. + * The values entry corresponding to a keyword is set to a palloc'd string + * containing the corresponding value, or NULL if the keyword does not appear. + */ +void +parseRelOptions(Datum options, int numkeywords, const char * const *keywords, + char **values, bool validate) +{ + ArrayType *array; + Datum *optiondatums; + int noptions; + int i; + + /* Initialize to "all defaulted" */ + MemSet(values, 0, numkeywords * sizeof(char *)); + + /* Done if no options */ + if (options == (Datum) 0) + return; + + array = DatumGetArrayTypeP(options); + + Assert(ARR_ELEMTYPE(array) == TEXTOID); + + deconstruct_array(array, TEXTOID, -1, false, 'i', + &optiondatums, NULL, &noptions); + + for (i = 0; i < noptions; i++) + { + text *optiontext = DatumGetTextP(optiondatums[i]); + char *text_str = (char *) VARATT_DATA(optiontext); + int text_len = VARATT_SIZE(optiontext) - VARHDRSZ; + int j; + + /* Search for a match in keywords */ + for (j = 0; j < numkeywords; j++) + { + int kw_len = strlen(keywords[j]); + + if (text_len > kw_len && text_str[kw_len] == '=' && + pg_strncasecmp(text_str, keywords[j], kw_len) == 0) + { + char *value; + int value_len; + + if (values[j] && validate) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("duplicate parameter \"%s\"", + keywords[j]))); + value_len = text_len - kw_len - 1; + value = (char *) palloc(value_len + 1); + memcpy(value, text_str + kw_len + 1, value_len); + value[value_len] = '\0'; + values[j] = value; + break; + } + } + if (j >= numkeywords && validate) + { + char *s; + char *p; + + s = DatumGetCString(DirectFunctionCall1(textout, optiondatums[i])); + p = strchr(s, '='); + if (p) + *p = '\0'; + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized parameter \"%s\"", s))); + } + } +} + + +/* + * Parse reloptions for anything using StdRdOptions (ie, fillfactor only) + */ +bytea * +default_reloptions(Datum reloptions, bool validate, + int minFillfactor, int defaultFillfactor) +{ + static const char * const default_keywords[1] = { "fillfactor" }; + char *values[1]; + int32 fillfactor; + StdRdOptions *result; + + parseRelOptions(reloptions, 1, default_keywords, values, validate); + + /* + * If no options, we can just return NULL rather than doing anything. + * (defaultFillfactor is thus not used, but we require callers to pass + * it anyway since we would need it if more options were added.) + */ + if (values[0] == NULL) + return NULL; + + fillfactor = pg_atoi(values[0], sizeof(int32), 0); + if (fillfactor < minFillfactor || fillfactor > 100) + { + if (validate) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("fillfactor=%d is out of range (should be between %d and 100)", + fillfactor, minFillfactor))); + return NULL; + } + + result = (StdRdOptions *) palloc(sizeof(StdRdOptions)); + VARATT_SIZEP(result) = sizeof(StdRdOptions); + + result->fillfactor = fillfactor; + + return (bytea *) result; +} + + +/* + * Parse options for heaps (and perhaps someday toast tables). + */ +bytea * +heap_reloptions(char relkind, Datum reloptions, bool validate) +{ + return default_reloptions(reloptions, validate, + HEAP_MIN_FILLFACTOR, + HEAP_DEFAULT_FILLFACTOR); +} + + +/* + * Parse options for indexes. + * + * amoptions Oid of option parser + * reloptions options as text[] datum + * validate error flag + */ +bytea * +index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate) +{ + FmgrInfo flinfo; + FunctionCallInfoData fcinfo; + Datum result; + + Assert(RegProcedureIsValid(amoptions)); + + /* Assume function is strict */ + if (reloptions == (Datum) 0) + return NULL; + + /* Can't use OidFunctionCallN because we might get a NULL result */ + fmgr_info(amoptions, &flinfo); + + InitFunctionCallInfoData(fcinfo, &flinfo, 2, NULL, NULL); + + fcinfo.arg[0] = reloptions; + fcinfo.arg[1] = BoolGetDatum(validate); + fcinfo.argnull[0] = false; + fcinfo.argnull[1] = false; + + result = FunctionCallInvoke(&fcinfo); + + if (fcinfo.isnull || DatumGetPointer(result) == NULL) + return NULL; + + return DatumGetByteaP(result); +} diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index 17e041bc4a..544956c84e 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.2 2006/07/02 02:23:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.3 2006/07/03 22:45:36 tgl Exp $ *------------------------------------------------------------------------- */ @@ -16,7 +16,7 @@ #include "access/genam.h" #include "access/gin.h" #include "access/heapam.h" -#include "catalog/index.h" +#include "access/reloptions.h" #include "miscadmin.h" #include "storage/freespace.h" @@ -203,15 +203,23 @@ GinPageGetCopyPage( Page page ) { } Datum -ginoption(PG_FUNCTION_ARGS) +ginoptions(PG_FUNCTION_ARGS) { - ArrayType *options = (ArrayType *) PG_GETARG_POINTER(0); - - if (options != NULL) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("GIN does not support parameters at all"))); - - /* Do not use PG_RETURN_NULL. */ - PG_RETURN_BYTEA_P(NULL); + Datum reloptions = PG_GETARG_DATUM(0); + bool validate = PG_GETARG_BOOL(1); + bytea *result; + + /* + * It's not clear that fillfactor is useful for GIN, but for the moment + * we'll accept it anyway. (It won't do anything...) + */ +#define GIN_MIN_FILLFACTOR 50 +#define GIN_DEFAULT_FILLFACTOR 100 + + result = default_reloptions(reloptions, validate, + GIN_MIN_FILLFACTOR, + GIN_DEFAULT_FILLFACTOR); + if (result) + PG_RETURN_BYTEA_P(result); + PG_RETURN_NULL(); } diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 4137ab4426..bc915332b5 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.140 2006/07/02 02:23:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.141 2006/07/03 22:45:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -197,9 +197,13 @@ gistbuildCallback(Relation index, * which locks the relation for write. This is the right thing to do if * you're inserting single tups, but not when you're initializing the * whole index at once. + * + * In this path we respect the fillfactor setting, whereas insertions + * after initial build do not. */ - gistdoinsert(index, itup, IndexGetPageFreeSpace(index), - &buildstate->giststate); + gistdoinsert(index, itup, + RelationGetTargetPageFreeSpace(index, GIST_DEFAULT_FILLFACTOR), + &buildstate->giststate); buildstate->indtuples += 1; MemoryContextSwitchTo(oldCtx); @@ -283,7 +287,6 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate) bool is_splitted = false; bool is_leaf = (GistPageIsLeaf(state->stack->page)) ? true : false; - /* * if (!is_leaf) remove old key: * This node's key has been modified, either because a child split @@ -294,14 +297,13 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate) * setting up a one-element todelete array; in the split case, it's * handled implicitly because the tuple vector passed to gistSplit * won't include this tuple. - */ - - - /* + * * XXX: If we want to change fillfactors between node and leaf, * fillfactor = (is_leaf ? state->leaf_fillfactor : state->node_fillfactor) */ - if (gistnospace(state->stack->page, state->itup, state->ituplen, (is_leaf) ? InvalidOffsetNumber : state->stack->childoffnum, state->freespace)) + if (gistnospace(state->stack->page, state->itup, state->ituplen, + is_leaf ? InvalidOffsetNumber : state->stack->childoffnum, + state->freespace)) { /* no space for insertion */ IndexTuple *itvec; diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index ae1fbc7320..33cdae37c3 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.17 2006/07/02 02:23:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.18 2006/07/03 22:45:36 tgl Exp $ *------------------------------------------------------------------------- */ #include "postgres.h" @@ -17,7 +17,7 @@ #include "access/gist_private.h" #include "access/gistscan.h" #include "access/heapam.h" -#include "catalog/index.h" +#include "access/reloptions.h" #include "miscadmin.h" #include "storage/freespace.h" @@ -637,14 +637,16 @@ gistNewBuffer(Relation r) } Datum -gistoption(PG_FUNCTION_ARGS) +gistoptions(PG_FUNCTION_ARGS) { -#define GIST_DEFAULT_FILLFACTOR 90 -#define GIST_MIN_FILLFACTOR 50 - - ArrayType *options = (ArrayType *) PG_GETARG_POINTER(0); - - /* Use index common routine. */ - PG_RETURN_BYTEA_P(genam_option(options, - GIST_MIN_FILLFACTOR, GIST_DEFAULT_FILLFACTOR)); + Datum reloptions = PG_GETARG_DATUM(0); + bool validate = PG_GETARG_BOOL(1); + bytea *result; + + result = default_reloptions(reloptions, validate, + GIST_MIN_FILLFACTOR, + GIST_DEFAULT_FILLFACTOR); + if (result) + PG_RETURN_BYTEA_P(result); + PG_RETURN_NULL(); } diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c index bed0b5dbd7..f5a1fcfd81 100644 --- a/src/backend/access/hash/hashpage.c +++ b/src/backend/access/hash/hashpage.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/hash/hashpage.c,v 1.58 2006/07/02 02:23:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/hash/hashpage.c,v 1.59 2006/07/03 22:45:36 tgl Exp $ * * NOTES * Postgres hash pages look like ordinary relation pages. The opaque @@ -30,7 +30,6 @@ #include "access/genam.h" #include "access/hash.h" -#include "catalog/index.h" #include "miscadmin.h" #include "storage/lmgr.h" #include "utils/lsyscache.h" @@ -223,16 +222,17 @@ _hash_metapinit(Relation rel) RelationGetRelationName(rel)); /* - * Determine the target fill factor (tuples per bucket) for this index. - * The idea is to make the fill factor correspond to pages about 3/4ths - * full. We can compute it exactly if the index datatype is fixed-width, - * but for var-width there's some guessing involved. + * Determine the target fill factor (in tuples per bucket) for this index. + * The idea is to make the fill factor correspond to pages about as full + * as the user-settable fillfactor parameter says. We can compute it + * exactly if the index datatype is fixed-width, but for var-width there's + * some guessing involved. */ data_width = get_typavgwidth(RelationGetDescr(rel)->attrs[0]->atttypid, RelationGetDescr(rel)->attrs[0]->atttypmod); item_width = MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_width) + sizeof(ItemIdData); /* include the line pointer */ - ffactor = BLCKSZ * IndexGetFillFactor(rel) / 100 / item_width; + ffactor = RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR) / item_width; /* keep to a sane range */ if (ffactor < 10) ffactor = 10; diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c index fbee1fdc2a..a1a03ddcf3 100644 --- a/src/backend/access/hash/hashutil.c +++ b/src/backend/access/hash/hashutil.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/hash/hashutil.c,v 1.48 2006/07/02 02:23:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/hash/hashutil.c,v 1.49 2006/07/03 22:45:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,6 +16,7 @@ #include "access/genam.h" #include "access/hash.h" +#include "access/reloptions.h" #include "executor/execdebug.h" @@ -175,14 +176,16 @@ _hash_checkpage(Relation rel, Buffer buf, int flags) } Datum -hashoption(PG_FUNCTION_ARGS) +hashoptions(PG_FUNCTION_ARGS) { -#define HASH_MIN_FILLFACTOR 50 -#define HASH_DEFAULT_FILLFACTOR 75 - - ArrayType *options = (ArrayType *) PG_GETARG_POINTER(0); - - /* Use index common routine. */ - PG_RETURN_BYTEA_P(genam_option(options, - HASH_MIN_FILLFACTOR, HASH_DEFAULT_FILLFACTOR)); + Datum reloptions = PG_GETARG_DATUM(0); + bool validate = PG_GETARG_BOOL(1); + bytea *result; + + result = default_reloptions(reloptions, validate, + HASH_MIN_FILLFACTOR, + HASH_DEFAULT_FILLFACTOR); + if (result) + PG_RETURN_BYTEA_P(result); + PG_RETURN_NULL(); } diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index cf3344c200..cf4b920f7d 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.214 2006/07/02 02:23:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.215 2006/07/03 22:45:37 tgl Exp $ * * * INTERFACE ROUTINES @@ -46,13 +46,9 @@ #include "access/xlogutils.h" #include "catalog/catalog.h" #include "catalog/namespace.h" -#include "commands/defrem.h" #include "miscadmin.h" -#include "nodes/parsenodes.h" -#include "parser/parse_clause.h" #include "pgstat.h" #include "storage/procarray.h" -#include "utils/catcache.h" #include "utils/inval.h" #include "utils/relcache.h" @@ -3592,59 +3588,3 @@ heap_desc(StringInfo buf, uint8 xl_info, char *rec) else appendStringInfo(buf, "UNKNOWN"); } - -/* - * Parse options for heaps. - * - * relkind Kind of relation - * options Options as text[] - */ -bytea * -heap_option(char relkind, ArrayType *options) -{ - /* - * XXX: What fillfactor should be default? - * overriding databases: - * - Oracle, DB2 = 90% - * - SQL Server = 100% - * non-overriding database: - * - Firebird = 70% - */ -#define HEAP_MIN_FILLFACTOR 50 -#define HEAP_DEFAULT_FILLFACTOR 100 - - int fillfactor; - HeapOption *result; - - DefElem kwds[] = - { - { T_DefElem, "fillfactor" }, - }; - - /* - * parse options - */ - OptionParse(options, lengthof(kwds), kwds, true); - - /* 0: fillfactor */ - if (kwds[0].arg) - fillfactor = (int) defGetInt64(&kwds[0]); - else - fillfactor = HEAP_DEFAULT_FILLFACTOR; - if (fillfactor < HEAP_MIN_FILLFACTOR || 100 < fillfactor) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("fillfactor=%d should be between %d and 100", - fillfactor, HEAP_MIN_FILLFACTOR))); - } - - /* - * build option - */ - result = (HeapOption *) - MemoryContextAlloc(CacheMemoryContext, sizeof(HeapOption)); - VARATT_SIZEP(result) = sizeof(HeapOption); - result->fillfactor = fillfactor; - return (bytea *) result; -} diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c index 82fb0a3268..aaaec863ba 100644 --- a/src/backend/access/heap/hio.c +++ b/src/backend/access/heap/hio.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.62 2006/07/02 02:23:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.63 2006/07/03 22:45:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -93,6 +93,11 @@ RelationPutHeapTuple(Relation relation, * any committed data of other transactions. (See heap_insert's comments * for additional constraints needed for safe usage of this behavior.) * + * We always try to avoid filling existing pages further than the fillfactor. + * This is OK since this routine is not consulted when updating a tuple and + * keeping it on the same page, which is the scenario fillfactor is meant + * to reserve space for. + * * ereport(ERROR) is allowed here, so this routine *must* be called * before any (unlogged) changes are made in buffer pool. */ @@ -103,17 +108,12 @@ RelationGetBufferForTuple(Relation relation, Size len, Buffer buffer = InvalidBuffer; Page pageHeader; Size pageFreeSpace, - freespace; + saveFreeSpace; BlockNumber targetBlock, otherBlock; bool needLock; - if (relation->rd_options == NULL) - elog(ERROR, "RelationGetBufferForTuple %s IS NULL", RelationGetRelationName(relation)); - Assert(relation->rd_options != NULL); - len = MAXALIGN(len); /* be conservative */ - freespace = HeapGetPageFreeSpace(relation); /* * If we're gonna fail for oversize tuple, do it right away @@ -125,6 +125,10 @@ RelationGetBufferForTuple(Relation relation, Size len, (unsigned long) len, (unsigned long) MaxTupleSize))); + /* Compute desired extra freespace due to fillfactor option */ + saveFreeSpace = RelationGetTargetPageFreeSpace(relation, + HEAP_DEFAULT_FILLFACTOR); + if (otherBuffer != InvalidBuffer) otherBlock = BufferGetBlockNumber(otherBuffer); else @@ -143,8 +147,14 @@ RelationGetBufferForTuple(Relation relation, Size len, * When use_fsm is false, we either put the tuple onto the existing target * page or extend the relation. */ - - targetBlock = relation->rd_targblock; + if (len + saveFreeSpace <= MaxTupleSize) + targetBlock = relation->rd_targblock; + else + { + /* can't fit, don't screw up FSM request tracking by trying */ + targetBlock = InvalidBlockNumber; + use_fsm = false; + } if (targetBlock == InvalidBlockNumber && use_fsm) { @@ -152,7 +162,8 @@ RelationGetBufferForTuple(Relation relation, Size len, * We have no cached target page, so ask the FSM for an initial * target. */ - targetBlock = GetPageWithFreeSpace(&relation->rd_node, len + freespace); + targetBlock = GetPageWithFreeSpace(&relation->rd_node, + len + saveFreeSpace); /* * If the FSM knows nothing of the rel, try the last page before we @@ -208,7 +219,7 @@ RelationGetBufferForTuple(Relation relation, Size len, */ pageHeader = (Page) BufferGetPage(buffer); pageFreeSpace = PageGetFreeSpace(pageHeader); - if (len + freespace <= pageFreeSpace) + if (len + saveFreeSpace <= pageFreeSpace) { /* use this page as future insert target, too */ relation->rd_targblock = targetBlock; @@ -241,7 +252,7 @@ RelationGetBufferForTuple(Relation relation, Size len, targetBlock = RecordAndGetPageWithFreeSpace(&relation->rd_node, targetBlock, pageFreeSpace, - len + freespace); + len + saveFreeSpace); } /* diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index cfc2a833cd..399386c856 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.56 2006/07/02 02:23:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.57 2006/07/03 22:45:37 tgl Exp $ * * NOTES * many of the old access method routines have been turned into @@ -21,12 +21,8 @@ #include "access/genam.h" #include "access/heapam.h" -#include "commands/defrem.h" #include "miscadmin.h" -#include "nodes/parsenodes.h" -#include "parser/parse_clause.h" #include "pgstat.h" -#include "utils/catcache.h" /* ---------------------------------------------------------------- @@ -264,44 +260,3 @@ systable_endscan(SysScanDesc sysscan) pfree(sysscan); } - -/* - * Parse options for generic indexes. - */ -bytea * -genam_option(ArrayType *options, - int minFillfactor, int defaultFillfactor) -{ - int fillfactor; - IndexOption *result; - - DefElem kwds[] = - { - { T_DefElem, "fillfactor" }, - }; - - /* - * parse options - */ - OptionParse(options, lengthof(kwds), kwds, true); - - /* 0: fillfactor */ - if (kwds[0].arg) - fillfactor = (int) defGetInt64(&kwds[0]); - else - fillfactor = defaultFillfactor; - if (fillfactor < minFillfactor || 100 < fillfactor) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("fillfactor=%d should be between %d and 100", - fillfactor, minFillfactor))); - - /* - * build options - */ - result = (IndexOption *) - MemoryContextAlloc(CacheMemoryContext, sizeof(IndexOption)); - VARATT_SIZEP(result) = sizeof(IndexOption); - result->fillfactor = fillfactor; - return (bytea *) result; -} diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c index 3b78843101..a176888691 100644 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -8,14 +8,13 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.138 2006/07/02 02:23:18 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.139 2006/07/03 22:45:37 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "access/genam.h" #include "access/heapam.h" #include "access/nbtree.h" #include "miscadmin.h" @@ -26,7 +25,7 @@ typedef struct { /* context data for _bt_checksplitloc */ Size newitemsz; /* size of new item to be inserted */ - int fillfactor; /* used when insert at right most */ + int fillfactor; /* needed when splitting rightmost page */ bool is_leaf; /* T if splitting a leaf page */ bool is_rightmost; /* T if splitting a rightmost page */ @@ -988,11 +987,11 @@ _bt_split(Relation rel, Buffer buf, OffsetNumber firstright, * it needs to go into!) * * If the page is the rightmost page on its level, we instead try to arrange - * for reserving (100-fillfactor)% of free space on left page. In this way, - * when we are inserting successively increasing keys (consider sequences, - * timestamps, etc) we will end up with a tree whose pages are about fillfactor% full, + * to leave the left split page fillfactor% full. In this way, when we are + * inserting successively increasing keys (consider sequences, timestamps, + * etc) we will end up with a tree whose pages are about fillfactor% full, * instead of the 50% full result that we'd get without this special case. - * This is the same as initially-loaded tree. + * This is the same as nbtsort.c produces for a newly-created tree. * * We are passed the intended insert position of the new tuple, expressed as * the offsetnumber of the tuple it must go in front of. (This could be @@ -1026,7 +1025,7 @@ _bt_findsplitloc(Relation rel, /* Passed-in newitemsz is MAXALIGNED but does not include line pointer */ newitemsz += sizeof(ItemIdData); state.newitemsz = newitemsz; - state.fillfactor = IndexGetFillFactor(rel); + state.fillfactor = RelationGetFillFactor(rel, BTREE_DEFAULT_FILLFACTOR); state.is_leaf = P_ISLEAF(opaque); state.is_rightmost = P_RIGHTMOST(opaque); state.have_split = false; @@ -1157,7 +1156,7 @@ _bt_checksplitloc(FindSplitData *state, OffsetNumber firstright, if (state->is_rightmost) { /* - * On a rightmost page, try to reserve (100-fillfactor)% of + * If splitting a rightmost page, try to put (100-fillfactor)% of * free space on left page. See comments for _bt_findsplitloc. */ delta = (state->fillfactor * leftfree) diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c index 05785d98eb..1dd3be0a63 100644 --- a/src/backend/access/nbtree/nbtsort.c +++ b/src/backend/access/nbtree/nbtsort.c @@ -27,9 +27,10 @@ * insertion would cause a split (and not only of the leaf page; the need * for a split would cascade right up the tree). The steady-state load * factor for btrees is usually estimated at 70%. We choose to pack leaf - * pages to 90% and upper pages to 70%. This gives us reasonable density - * (there aren't many upper pages if the keys are reasonable-size) without - * incurring a lot of cascading splits during early insertions. + * pages to the user-controllable fill factor while upper pages are always + * packed to 70%. This gives us reasonable density (there aren't many upper + * pages if the keys are reasonable-size) without incurring a lot of cascading + * splits during early insertions. * * Formerly the index pages being built were kept in shared buffers, but * that is of no value (since other backends have no interest in them yet) @@ -56,14 +57,13 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsort.c,v 1.103 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsort.c,v 1.104 2006/07/03 22:45:37 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "access/genam.h" #include "access/nbtree.h" #include "access/xlog.h" #include "miscadmin.h" @@ -121,7 +121,6 @@ typedef struct BTWriteState static Page _bt_blnewpage(uint32 level); -static Size _bt_full_threshold(Relation index, Size pagesize, bool leaf); static BTPageState *_bt_pagestate(BTWriteState *wstate, uint32 level); static void _bt_slideleft(Page page); static void _bt_sortaddtup(Page page, Size itemsize, @@ -330,22 +329,6 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno) } /* - * The steady-state load factor for btrees is usually estimated at 70%. - * We choose to pack leaf pages to 90% and upper pages to 70% as defaults. - */ -static Size -_bt_full_threshold(Relation index, Size pagesize, bool leaf) -{ - int fillfactor = IndexGetFillFactor(index); - if (!leaf) - { - /* XXX: Is this reasonable? */ - fillfactor = Max(70, 3 * fillfactor - 200); - } - return pagesize * (100 - fillfactor) / 100; -} - -/* * allocate and initialize a new BTPageState. the returned structure * is suitable for immediate use by _bt_buildadd. */ @@ -365,8 +348,11 @@ _bt_pagestate(BTWriteState *wstate, uint32 level) state->btps_lastoff = P_HIKEY; state->btps_level = level; /* set "full" threshold based on level. See notes at head of file. */ - state->btps_full = _bt_full_threshold(wstate->index, - PageGetPageSize(state->btps_page), level == 0); + if (level > 0) + state->btps_full = (BLCKSZ * (100 - BTREE_MIN_FILLFACTOR) / 100); + else + state->btps_full = RelationGetTargetPageFreeSpace(wstate->index, + BTREE_DEFAULT_FILLFACTOR); /* no parent level, yet */ state->btps_next = NULL; diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index 93a9b0df65..6750a982d7 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.75 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.76 2006/07/03 22:45:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,7 +19,7 @@ #include "access/genam.h" #include "access/nbtree.h" -#include "catalog/catalog.h" +#include "access/reloptions.h" #include "executor/execdebug.h" #include "miscadmin.h" @@ -1081,14 +1081,16 @@ BTreeShmemInit(void) } Datum -btoption(PG_FUNCTION_ARGS) +btoptions(PG_FUNCTION_ARGS) { -#define BTREE_MIN_FILLFACTOR 50 -#define BTREE_DEFAULT_FILLFACTOR 90 - - ArrayType *options = (ArrayType *) PG_GETARG_POINTER(0); - - /* Use index common routine. */ - PG_RETURN_BYTEA_P(genam_option(options, - BTREE_MIN_FILLFACTOR, BTREE_DEFAULT_FILLFACTOR)); + Datum reloptions = PG_GETARG_DATUM(0); + bool validate = PG_GETARG_BOOL(1); + bytea *result; + + result = default_reloptions(reloptions, validate, + BTREE_MIN_FILLFACTOR, + BTREE_DEFAULT_FILLFACTOR); + if (result) + PG_RETURN_BYTEA_P(result); + PG_RETURN_NULL(); } diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c index da30e39b42..b6fd45d6df 100644 --- a/src/backend/access/transam/xlogutils.c +++ b/src/backend/access/transam/xlogutils.c @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.45 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.46 2006/07/03 22:45:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -337,7 +337,7 @@ _xl_remove_hash_entry(XLogRelDesc *rdesc) RelationCloseSmgr(&(rdesc->reldata)); memset(rdesc, 0, sizeof(XLogRelDesc)); - memset(tpgc, 0, CLASS_TUPLE_SIZE); + memset(tpgc, 0, sizeof(FormData_pg_class)); rdesc->reldata.rd_rel = tpgc; } diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index 901c392d7e..9bb8f01b1e 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.81 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.82 2006/07/03 22:45:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,7 +19,6 @@ #include <unistd.h> #include "access/attnum.h" -#include "access/heapam.h" #include "access/htup.h" #include "access/itup.h" #include "access/skey.h" @@ -193,8 +192,6 @@ Boot_CreateStmt: RELKIND_RELATION, $3, true); - boot_reldesc->rd_options = - heap_option(RELKIND_RELATION, NULL); elog(DEBUG4, "bootstrap relation created"); } else @@ -212,8 +209,8 @@ Boot_CreateStmt: true, 0, ONCOMMIT_NOOP, - true, - NULL); + (Datum) 0, + true); elog(DEBUG4, "relation created with oid %u", id); } do_end(); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index e2eed023e0..3a6bc46a69 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.303 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.304 2006/07/03 22:45:37 tgl Exp $ * * * INTERFACE ROUTINES @@ -44,24 +44,20 @@ #include "catalog/pg_type.h" #include "commands/tablecmds.h" #include "commands/trigger.h" -#include "commands/defrem.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "optimizer/planmain.h" #include "optimizer/var.h" #include "parser/parse_coerce.h" -#include "parser/parse_clause.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" #include "rewrite/rewriteRemove.h" #include "storage/smgr.h" -#include "utils/catcache.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/inval.h" #include "utils/lsyscache.h" -#include "utils/memutils.h" #include "utils/relcache.h" #include "utils/syscache.h" @@ -71,7 +67,7 @@ static void AddNewRelationTuple(Relation pg_class_desc, Oid new_rel_oid, Oid new_type_oid, Oid relowner, char relkind, - ArrayType *options); + Datum reloptions); static Oid AddNewRelationType(const char *typeName, Oid typeNamespace, Oid new_rel_oid, @@ -551,6 +547,80 @@ AddNewAttributeTuples(Oid new_rel_oid, } /* -------------------------------- + * InsertPgClassTuple + * + * Construct and insert a new tuple in pg_class. + * + * Caller has already opened and locked pg_class. + * Tuple data is taken from new_rel_desc->rd_rel, except for the + * variable-width fields which are not present in a cached reldesc. + * We alway initialize relacl to NULL (i.e., default permissions), + * and reloptions is set to the passed-in text array (if any). + * -------------------------------- + */ +void +InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, + Datum reloptions) +{ + Form_pg_class rd_rel = new_rel_desc->rd_rel; + Datum values[Natts_pg_class]; + char nulls[Natts_pg_class]; + HeapTuple tup; + + /* This is a tad tedious, but way cleaner than what we used to do... */ + memset(values, 0, sizeof(values)); + memset(nulls, ' ', sizeof(nulls)); + + values[Anum_pg_class_relname - 1] = NameGetDatum(&rd_rel->relname); + values[Anum_pg_class_relnamespace - 1] = ObjectIdGetDatum(rd_rel->relnamespace); + values[Anum_pg_class_reltype - 1] = ObjectIdGetDatum(rd_rel->reltype); + values[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(rd_rel->relowner); + values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(rd_rel->relam); + values[Anum_pg_class_relfilenode - 1] = ObjectIdGetDatum(rd_rel->relfilenode); + values[Anum_pg_class_reltablespace - 1] = ObjectIdGetDatum(rd_rel->reltablespace); + values[Anum_pg_class_relpages - 1] = Int32GetDatum(rd_rel->relpages); + values[Anum_pg_class_reltuples - 1] = Float4GetDatum(rd_rel->reltuples); + values[Anum_pg_class_reltoastrelid - 1] = ObjectIdGetDatum(rd_rel->reltoastrelid); + values[Anum_pg_class_reltoastidxid - 1] = ObjectIdGetDatum(rd_rel->reltoastidxid); + values[Anum_pg_class_relhasindex - 1] = BoolGetDatum(rd_rel->relhasindex); + values[Anum_pg_class_relisshared - 1] = BoolGetDatum(rd_rel->relisshared); + values[Anum_pg_class_relkind - 1] = CharGetDatum(rd_rel->relkind); + values[Anum_pg_class_relnatts - 1] = Int16GetDatum(rd_rel->relnatts); + values[Anum_pg_class_relchecks - 1] = Int16GetDatum(rd_rel->relchecks); + values[Anum_pg_class_reltriggers - 1] = Int16GetDatum(rd_rel->reltriggers); + values[Anum_pg_class_relukeys - 1] = Int16GetDatum(rd_rel->relukeys); + values[Anum_pg_class_relfkeys - 1] = Int16GetDatum(rd_rel->relfkeys); + values[Anum_pg_class_relrefs - 1] = Int16GetDatum(rd_rel->relrefs); + values[Anum_pg_class_relhasoids - 1] = BoolGetDatum(rd_rel->relhasoids); + values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey); + values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules); + values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass); + /* start out with empty permissions */ + nulls[Anum_pg_class_relacl - 1] = 'n'; + if (reloptions != (Datum) 0) + values[Anum_pg_class_reloptions - 1] = reloptions; + else + nulls[Anum_pg_class_reloptions - 1] = 'n'; + + tup = heap_formtuple(RelationGetDescr(pg_class_desc), values, nulls); + + /* + * The new tuple must have the oid already chosen for the rel. Sure + * would be embarrassing to do this sort of thing in polite company. + */ + HeapTupleSetOid(tup, new_rel_oid); + + /* finally insert the new tuple, update the indexes, and clean up */ + simple_heap_insert(pg_class_desc, tup); + + CatalogUpdateIndexes(pg_class_desc, tup); + + heap_freetuple(tup); +} + +/* -------------------------------- * AddNewRelationTuple * * this registers the new relation in the catalogs by @@ -564,10 +634,9 @@ AddNewRelationTuple(Relation pg_class_desc, Oid new_type_oid, Oid relowner, char relkind, - ArrayType *options) + Datum reloptions) { Form_pg_class new_rel_reltup; - HeapTuple tup; /* * first we update some of the information in our uncataloged relation's @@ -602,20 +671,8 @@ AddNewRelationTuple(Relation pg_class_desc, new_rel_desc->rd_att->tdtypeid = new_type_oid; - /* now form a tuple to add to pg_class */ - tup = build_class_tuple(new_rel_reltup, options); - - /* force tuple to have the desired OID */ - HeapTupleSetOid(tup, new_rel_oid); - - /* - * finally insert the new tuple, update the indexes, and clean up. - */ - simple_heap_insert(pg_class_desc, tup); - - CatalogUpdateIndexes(pg_class_desc, tup); - - heap_freetuple(tup); + /* Now build and insert the tuple */ + InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions); } @@ -660,8 +717,6 @@ AddNewRelationType(const char *typeName, * heap_create_with_catalog * * creates a new cataloged relation. see comments above. - * - * if opaque is specified, it must be allocated in CacheMemoryContext. * -------------------------------- */ Oid @@ -676,12 +731,11 @@ heap_create_with_catalog(const char *relname, bool oidislocal, int oidinhcount, OnCommitAction oncommit, - bool allow_system_table_mods, - ArrayType *options) + Datum reloptions, + bool allow_system_table_mods) { Relation pg_class_desc; Relation new_rel_desc; - bytea *new_rel_options; Oid new_type_oid; pg_class_desc = heap_open(RelationRelationId, RowExclusiveLock); @@ -699,13 +753,6 @@ heap_create_with_catalog(const char *relname, errmsg("relation \"%s\" already exists", relname))); /* - * Parse options to check if option is valid. - */ - new_rel_options = heap_option(relkind, options); - Assert(!new_rel_options || - GetMemoryChunkContext(new_rel_options) == CacheMemoryContext); - - /* * Allocate an OID for the relation, unless we were told what to use. * * The OID will be the relfilenode as well, so make sure it doesn't @@ -728,7 +775,6 @@ heap_create_with_catalog(const char *relname, relkind, shared_relation, allow_system_table_mods); - new_rel_desc->rd_options = new_rel_options; Assert(relid == RelationGetRelid(new_rel_desc)); @@ -757,7 +803,7 @@ heap_create_with_catalog(const char *relname, new_type_oid, ownerid, relkind, - options); + reloptions); /* * now add tuples to pg_attribute for the attributes in our new relation. diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9143f6068a..f047f58b01 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.267 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.268 2006/07/03 22:45:37 tgl Exp $ * * * INTERFACE ROUTINES @@ -37,7 +37,6 @@ #include "executor/executor.h" #include "miscadmin.h" #include "optimizer/clauses.h" -#include "parser/parse_clause.h" #include "parser/parse_expr.h" #include "storage/procarray.h" #include "storage/smgr.h" @@ -54,8 +53,6 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, IndexInfo *indexInfo, Oid *classObjectId); -static void UpdateRelationRelation(Relation pg_class, Relation indexRelation, - ArrayType *options); static void InitializeAttributeOids(Relation indexRelation, int numatts, Oid indexoid); static void AppendAttributeTuples(Relation indexRelation, int numatts); @@ -239,32 +236,6 @@ ConstructTupleDescriptor(Relation heapRelation, } /* ---------------------------------------------------------------- - * UpdateRelationRelation - * ---------------------------------------------------------------- - */ -static void -UpdateRelationRelation(Relation pg_class, Relation indexRelation, - ArrayType *options) -{ - HeapTuple tuple; - - tuple = build_class_tuple(indexRelation->rd_rel, options); - - /* - * the new tuple must have the oid already chosen for the index. sure - * would be embarrassing to do this sort of thing in polite company. - */ - HeapTupleSetOid(tuple, RelationGetRelid(indexRelation)); - - simple_heap_insert(pg_class, tuple); - - /* update the system catalog indexes */ - CatalogUpdateIndexes(pg_class, tuple); - - heap_freetuple(tuple); -} - -/* ---------------------------------------------------------------- * InitializeAttributeOids * ---------------------------------------------------------------- */ @@ -449,6 +420,7 @@ UpdateIndexRelation(Oid indexoid, * accessMethodObjectId: OID of index AM to use * tableSpaceId: OID of tablespace to use * classObjectId: array of index opclass OIDs, one per index column + * reloptions: AM-specific options * isprimary: index is a PRIMARY KEY * istoast: index is a toast table's index * isconstraint: index is owned by a PRIMARY KEY or UNIQUE constraint @@ -466,7 +438,7 @@ index_create(Oid heapRelationId, Oid accessMethodObjectId, Oid tableSpaceId, Oid *classObjectId, - List *options, + Datum reloptions, bool isprimary, bool istoast, bool isconstraint, @@ -481,9 +453,6 @@ index_create(Oid heapRelationId, Oid namespaceId; int i; - ArrayType *array; - RegProcedure amoption; - pg_class = heap_open(RelationRelationId, RowExclusiveLock); /* @@ -582,40 +551,13 @@ index_create(Oid heapRelationId, indexRelation->rd_rel->relhasoids = false; /* - * AM specific options. - */ - array = OptionBuild(NULL, options); - if (indexRelation->rd_am) - { - amoption = indexRelation->rd_am->amoption; - } - else - { - HeapTuple tuple; - - /* - * We may use the access method before initializing relation, - * so we pick up AM from syscache directly. - */ - tuple = SearchSysCache(AMOID, - ObjectIdGetDatum(accessMethodObjectId), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "cache lookup failed for access method %u", - accessMethodObjectId); - amoption = ((Form_pg_am) GETSTRUCT(tuple))->amoption; - ReleaseSysCache(tuple); - } - indexRelation->rd_options = index_option(amoption, array); - - /* * store index's pg_class entry */ - UpdateRelationRelation(pg_class, indexRelation, array); + InsertPgClassTuple(pg_class, indexRelation, + RelationGetRelid(indexRelation), + reloptions); /* done with pg_class */ - if (array) - pfree(array); heap_close(pg_class, RowExclusiveLock); /* @@ -1783,23 +1725,3 @@ reindex_relation(Oid relid, bool toast_too) return result; } - -/* - * Parse options for indexes. - * - * amoption Oid of option parser. - * options Options as text[] - */ -bytea *index_option(RegProcedure amoption, ArrayType *options) -{ - Datum datum; - - Assert(RegProcedureIsValid(amoption)); - - datum = OidFunctionCall1(amoption, PointerGetDatum(options)); - - if (DatumGetPointer(datum) == NULL) - return NULL; - - return DatumGetByteaP(datum); -} diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index 2ed1a156a4..5724467d70 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -9,19 +9,16 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/indexing.c,v 1.112 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/indexing.c,v 1.113 2006/07/03 22:45:38 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/genam.h" -#include "access/heapam.h" #include "catalog/index.h" #include "catalog/indexing.h" #include "executor/executor.h" -#include "utils/syscache.h" -#include "commands/defrem.h" /* diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 08b76e96d5..fa41de3a2d 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.148 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.149 2006/07/03 22:45:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -567,7 +567,8 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace) Oid OIDNewHeap; Relation OldHeap; HeapTuple tuple; - ArrayType *options; + Datum reloptions; + bool isNull; OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock); OldHeapDesc = RelationGetDescr(OldHeap); @@ -584,19 +585,12 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace) tuple = SearchSysCache(RELOID, ObjectIdGetDatum(OIDOldHeap), 0, 0, 0); - if (tuple) - { - Datum datum; - bool isNull; - datum = SysCacheGetAttr(RELOID, tuple, - Anum_pg_class_reloptions, &isNull); - options = isNull ? NULL : DatumGetArrayTypeP(datum); - } - else - { - /* should not happen */ - options = NULL; - } + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap); + reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions, + &isNull); + if (isNull) + reloptions = (Datum) 0; OIDNewHeap = heap_create_with_catalog(NewName, RelationGetNamespace(OldHeap), @@ -609,8 +603,8 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace) true, 0, ONCOMMIT_NOOP, - allowSystemTableMods, - options); + reloptions, + allowSystemTableMods); ReleaseSysCache(tuple); diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c index ebde8f1095..149e1b6dae 100644 --- a/src/backend/commands/define.c +++ b/src/backend/commands/define.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/define.c,v 1.96 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/define.c,v 1.97 2006/07/03 22:45:38 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -110,7 +110,6 @@ defGetNumeric(DefElem *def) case T_Integer: return (double) intVal(def->arg); case T_Float: - case T_String: /* XXX: needs strict check? */ return floatVal(def->arg); default: ereport(ERROR, @@ -128,27 +127,39 @@ bool defGetBoolean(DefElem *def) { /* - * Presently, boolean flags must simply be present/absent or - * integer 0/1. Later we could allow 'flag = t', 'flag = f', etc. + * If no parameter given, assume "true" is meant. */ if (def->arg == NULL) return true; + /* + * Allow 0, 1, "true", "false" + */ switch (nodeTag(def->arg)) { case T_Integer: switch (intVal(def->arg)) { - case 0: - return false; - case 1: - return true; + case 0: + return false; + case 1: + return true; + default: + /* otherwise, error out below */ + break; } break; default: + { + char *sval = defGetString(def); + + if (pg_strcasecmp(sval, "true") == 0) + return true; + if (pg_strcasecmp(sval, "false") == 0) + return false; + + } break; } - - /* on error */ ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s requires a boolean value", @@ -172,7 +183,7 @@ defGetInt64(DefElem *def) case T_Integer: return (int64) intVal(def->arg); case T_Float: - case T_String: /* XXX: needs strict check? */ + /* * Values too large for int4 will be represented as Float * constants by the lexer. Accept these if they are valid int8 @@ -293,10 +304,14 @@ defGetTypeLength(DefElem *def) return 0; /* keep compiler quiet */ } +/* + * Create a DefElem setting "oids" to the specified value. + */ DefElem * defWithOids(bool value) { DefElem *f = makeNode(DefElem); + f->defname = "oids"; f->arg = (Node *)makeInteger(value); return f; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 7e35258ac0..d2c4bf8905 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.142 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.143 2006/07/03 22:45:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "access/genam.h" #include "access/heapam.h" +#include "access/reloptions.h" #include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/heap.h" @@ -72,12 +73,12 @@ static bool relationHasPrimaryKey(Relation rel); * to index on. * 'predicate': the partial-index condition, or NULL if none. * 'rangetable': needed to interpret the predicate. + * 'options': reloptions from WITH (in list-of-DefElem form). * 'unique': make the index enforce uniqueness. * 'primary': mark the index as a primary key in the catalogs. * 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint, * so build a pg_constraint entry for it. * 'is_alter_table': this is due to an ALTER rather than a CREATE operation. - * 'options': options passed by WITH. * 'check_rights': check for CREATE rights in the namespace. (This should * be true except when ALTER is deleting/recreating an index.) * 'skip_build': make the catalog entries but leave the index file empty; @@ -110,6 +111,8 @@ DefineIndex(RangeVar *heapRelation, Relation rel; HeapTuple tuple; Form_pg_am accessMethodForm; + RegProcedure amoptions; + Datum reloptions; IndexInfo *indexInfo; int numberOfAttributes; @@ -261,6 +264,8 @@ DefineIndex(RangeVar *heapRelation, errmsg("access method \"%s\" does not support multicolumn indexes", accessMethodName))); + amoptions = accessMethodForm->amoptions; + ReleaseSysCache(tuple); /* @@ -368,6 +373,13 @@ DefineIndex(RangeVar *heapRelation, } /* + * Parse AM-specific options, convert to text array form, validate. + */ + reloptions = transformRelOptions((Datum) 0, options, false, false); + + (void) index_reloptions(amoptions, reloptions, true); + + /* * Prepare arguments for index_create, primarily an IndexInfo structure. * Note that ii_Predicate must be in implicit-AND format. */ @@ -399,7 +411,7 @@ DefineIndex(RangeVar *heapRelation, index_create(relationId, indexRelationName, indexRelationId, indexInfo, accessMethodId, tablespaceId, classObjectId, - options, primary, false, isconstraint, + reloptions, primary, false, isconstraint, allowSystemTableMods, skip_build); } diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index 27ccb3fb87..78ec950f23 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -10,7 +10,7 @@ * Copyright (c) 2002-2006, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.54 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.55 2006/07/03 22:45:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -197,7 +197,6 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params, errmsg("prepared statement is not a SELECT"))); query->into = copyObject(stmt->into); query->intoOptions = copyObject(stmt->intoOptions); - query->intoHasOids = stmt->into_has_oids; query->intoOnCommit = stmt->into_on_commit; if (stmt->into_tbl_space) query->intoTableSpaceName = pstrdup(stmt->into_tbl_space); diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 65712c2092..cae956c22d 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.133 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.134 2006/07/03 22:45:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -205,17 +205,7 @@ DefineSequence(CreateSeqStmt *seq) /* Now form & insert sequence tuple */ tuple = heap_formtuple(tupDesc, value, null); - - { - /* - * HACK: Sequences insert only one tuple during initialize. - * We treat sequences as heaps then. - */ - HeapOption opaque = { sizeof(HeapOption), 100 }; - rel->rd_options = (bytea *) &opaque; - simple_heap_insert(rel, tuple); - rel->rd_options = NULL; - } + simple_heap_insert(rel, tuple); Assert(ItemPointerGetOffsetNumber(&(tuple->t_self)) == FirstOffsetNumber); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 7b2a0508fe..f67f1b62ec 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,13 +8,14 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.191 2006/07/02 05:17:26 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.192 2006/07/03 22:45:38 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/genam.h" +#include "access/reloptions.h" #include "access/tuptoaster.h" #include "catalog/catalog.h" #include "catalog/dependency.h" @@ -62,6 +63,7 @@ #include "utils/relcache.h" #include "utils/syscache.h" + /* * ON COMMIT action list */ @@ -248,7 +250,7 @@ static void ATExecDropCluster(Relation rel); static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename); static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace); -static void ATExecSetOptions(Relation rel, List *newOptions); +static void ATExecSetRelOptions(Relation rel, List *defList, bool isReset); static void ATExecEnableDisableTrigger(Relation rel, char *trigname, bool enable, bool skip_system); static void ATExecAddInherits(Relation rel, RangeVar *parent); @@ -283,10 +285,10 @@ DefineRelation(CreateStmt *stmt, char relkind) bool localHasOids; int parentOidCount; List *rawDefaults; + Datum reloptions; ListCell *listptr; int i; AttrNumber attnum; - ArrayType *options; /* * Truncate relname to appropriate length (probably a waste of time, as @@ -339,6 +341,13 @@ DefineRelation(CreateStmt *stmt, char relkind) /* note InvalidOid is OK in this case */ } + /* + * Parse and validate reloptions, if any. + */ + reloptions = transformRelOptions((Datum) 0, stmt->options, true, false); + + (void) heap_reloptions(relkind, reloptions, true); + /* Check permissions except when using database's default */ if (OidIsValid(tablespaceId)) { @@ -428,7 +437,6 @@ DefineRelation(CreateStmt *stmt, char relkind) } } - options = OptionBuild(NULL, stmt->options); relationId = heap_create_with_catalog(relname, namespaceId, tablespaceId, @@ -440,10 +448,8 @@ DefineRelation(CreateStmt *stmt, char relkind) localHasOids, parentOidCount, stmt->oncommit, - allowSystemTableMods, - options); - if (options) - pfree(options); + reloptions, + allowSystemTableMods); StoreCatalogInheritance(relationId, inheritOids); @@ -2103,7 +2109,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, ATPrepSetTableSpace(tab, rel, cmd->name); pass = AT_PASS_MISC; /* doesn't actually matter */ break; - case AT_SetOptions: /* SET (...) */ + case AT_SetRelOptions: /* SET (...) */ + case AT_ResetRelOptions: /* RESET (...) */ ATSimplePermissionsRelationOrIndex(rel); /* This command never recurses */ /* No command-specific prep needed */ @@ -2279,8 +2286,11 @@ ATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd) * Nothing to do here; Phase 3 does the work */ break; - case AT_SetOptions: /* SET (...) */ - ATExecSetOptions(rel, (List *) cmd->def); + case AT_SetRelOptions: /* SET (...) */ + ATExecSetRelOptions(rel, (List *) cmd->def, false); + break; + case AT_ResetRelOptions: /* RESET (...) */ + ATExecSetRelOptions(rel, (List *) cmd->def, true); break; case AT_EnableTrig: /* ENABLE TRIGGER name */ ATExecEnableDisableTrigger(rel, cmd->name, true, false); @@ -5757,24 +5767,29 @@ ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename) } /* - * ALTER TABLE/INDEX SET (...) + * ALTER TABLE/INDEX SET (...) or RESET (...) */ static void -ATExecSetOptions(Relation rel, List *newOptions) +ATExecSetRelOptions(Relation rel, List *defList, bool isReset) { Oid relid; Relation pgclass; HeapTuple tuple; + HeapTuple newtuple; Datum datum; bool isnull; - ArrayType *mergedOptions; - bytea *options; + Datum newOptions; + Datum repl_val[Natts_pg_class]; + char repl_null[Natts_pg_class]; + char repl_repl[Natts_pg_class]; - if (list_length(newOptions) == 0) - return; /* do nothing */ + if (defList == NIL) + return; /* nothing to do */ - relid = RelationGetRelid(rel); pgclass = heap_open(RelationRelationId, RowExclusiveLock); + + /* Get the old reloptions */ + relid = RelationGetRelid(rel); tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0); @@ -5783,59 +5798,54 @@ ATExecSetOptions(Relation rel, List *newOptions) datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions, &isnull); - mergedOptions = OptionBuild( - isnull ? NULL : DatumGetArrayTypeP(datum), newOptions); + /* Generate new proposed reloptions (text array) */ + newOptions = transformRelOptions(isnull ? (Datum) 0 : datum, + defList, false, isReset); + /* Validate */ switch (rel->rd_rel->relkind) { case RELKIND_RELATION: case RELKIND_TOASTVALUE: - options = heap_option(rel->rd_rel->relkind, mergedOptions); + (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true); break; case RELKIND_INDEX: - options = index_option(rel->rd_am->amoption, mergedOptions); + (void) index_reloptions(rel->rd_am->amoptions, newOptions, true); break; default: - elog(ERROR, "unexpected RELKIND=%c", rel->rd_rel->relkind); - options = NULL; /* keep compiler quiet */ + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, index, or TOAST table", + RelationGetRelationName(rel)))); break; } - if (rel->rd_options != options) - { - HeapTuple newtuple; - Datum repl_val[Natts_pg_class]; - char repl_null[Natts_pg_class]; - char repl_repl[Natts_pg_class]; - - /* XXX: This is not necessarily required. */ - if (rel->rd_options) - pfree(rel->rd_options); - rel->rd_options = options; + /* + * All we need do here is update the pg_class row; the new options will be + * propagated into relcaches during post-commit cache inval. + */ + memset(repl_val, 0, sizeof(repl_val)); + memset(repl_null, ' ', sizeof(repl_null)); + memset(repl_repl, ' ', sizeof(repl_repl)); - memset(repl_repl, ' ', sizeof(repl_repl)); - memset(repl_null, ' ', sizeof(repl_null)); - repl_repl[Anum_pg_class_reloptions - 1] = 'r'; + if (newOptions != (Datum) 0) + repl_val[Anum_pg_class_reloptions - 1] = newOptions; + else + repl_null[Anum_pg_class_reloptions - 1] = 'n'; - if (mergedOptions) - repl_val[Anum_pg_class_reloptions - 1] = - PointerGetDatum(mergedOptions); - else - repl_null[Anum_pg_class_reloptions - 1] = 'n'; + repl_repl[Anum_pg_class_reloptions - 1] = 'r'; - newtuple = heap_modifytuple(tuple, RelationGetDescr(pgclass), - repl_val, repl_null, repl_repl); + newtuple = heap_modifytuple(tuple, RelationGetDescr(pgclass), + repl_val, repl_null, repl_repl); - simple_heap_update(pgclass, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(pgclass, newtuple); + simple_heap_update(pgclass, &newtuple->t_self, newtuple); - heap_freetuple(newtuple); - } + CatalogUpdateIndexes(pgclass, newtuple); - if (mergedOptions) - pfree(mergedOptions); + heap_freetuple(newtuple); ReleaseSysCache(tuple); + heap_close(pgclass, RowExclusiveLock); } @@ -6642,6 +6652,9 @@ AlterTableCreateToastTable(Oid relOid, bool silent) * even if its master relation is a temp table. There cannot be any * naming collision, and the toast rel will be destroyed when its master * is, so there's no need to handle the toast rel as temp. + * + * XXX would it make sense to apply the master's reloptions to the toast + * table? */ toast_relid = heap_create_with_catalog(toast_relname, PG_TOAST_NAMESPACE, @@ -6654,8 +6667,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent) true, 0, ONCOMMIT_NOOP, - true, - NULL); + (Datum) 0, + true); /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); @@ -6689,7 +6702,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent) indexInfo, BTREE_AM_OID, rel->rd_rel->reltablespace, - classObjectId, NIL, + classObjectId, (Datum) 0, true, true, false, true, false); /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 24a93e998d..17a802dc30 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.331 2006/07/02 02:23:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.332 2006/07/03 22:45:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -3119,8 +3119,6 @@ vac_update_fsm(Relation onerel, VacPageList fraged_pages, * vacuumlazy.c does, we'd be skewing that statistic. */ threshold = GetAvgFSMRequestSize(&onerel->rd_node); - if (threshold < HeapGetPageFreeSpace(onerel)) - threshold = HeapGetPageFreeSpace(onerel); pageSpaces = (PageFreeSpaceInfo *) palloc(nPages * sizeof(PageFreeSpaceInfo)); @@ -3391,11 +3389,13 @@ static Size PageGetFreeSpaceWithFillFactor(Relation relation, Page page) { PageHeader pd = (PageHeader) page; - Size pagefree = HeapGetPageFreeSpace(relation); Size freespace = pd->pd_upper - pd->pd_lower; + Size targetfree; - if (freespace > pagefree) - return freespace - pagefree; + targetfree = RelationGetTargetPageFreeSpace(relation, + HEAP_DEFAULT_FILLFACTOR); + if (freespace > targetfree) + return freespace - targetfree; else return 0; } diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index 8e97fa4756..264cb43778 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -31,7 +31,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.71 2006/07/02 02:23:20 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.72 2006/07/03 22:45:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -149,8 +149,6 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt) /* Set threshold for interesting free space = average request size */ /* XXX should we scale it up or down? Adjust vacuum.c too, if so */ vacrelstats->threshold = GetAvgFSMRequestSize(&onerel->rd_node); - if (vacrelstats->threshold < HeapGetPageFreeSpace(onerel)) - vacrelstats->threshold = HeapGetPageFreeSpace(onerel); /* Open all indexes of the relation */ vac_open_indexes(onerel, ShareUpdateExclusiveLock, &nindexes, &Irel); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index bb03b9358c..3c9f0798ba 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,13 +26,14 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.272 2006/07/02 02:23:20 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.273 2006/07/03 22:45:38 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/heapam.h" +#include "access/reloptions.h" #include "access/xlog.h" #include "catalog/heap.h" #include "catalog/namespace.h" @@ -45,8 +46,8 @@ #include "miscadmin.h" #include "optimizer/clauses.h" #include "optimizer/var.h" -#include "parser/parse_clause.h" #include "parser/parsetree.h" +#include "parser/parse_clause.h" #include "storage/smgr.h" #include "utils/acl.h" #include "utils/guc.h" @@ -543,7 +544,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) { do_select_into = true; estate->es_select_into = true; - estate->es_into_oids = parseTree->intoHasOids; + estate->es_into_oids = interpretOidsOption(parseTree->intoOptions); } /* @@ -727,10 +728,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) char *intoName; Oid namespaceId; Oid tablespaceId; + Datum reloptions; AclResult aclresult; Oid intoRelationId; TupleDesc tupdesc; - ArrayType *options; /* * Check consistency of arguments @@ -770,6 +771,13 @@ InitPlan(QueryDesc *queryDesc, int eflags) /* note InvalidOid is OK in this case */ } + /* Parse and validate any reloptions */ + reloptions = transformRelOptions((Datum) 0, + parseTree->intoOptions, + true, + false); + (void) heap_reloptions(RELKIND_RELATION, reloptions, true); + /* Check permissions except when using the database's default */ if (OidIsValid(tablespaceId)) { @@ -788,7 +796,6 @@ InitPlan(QueryDesc *queryDesc, int eflags) */ tupdesc = CreateTupleDescCopy(tupType); - options = OptionBuild(NULL, parseTree->intoOptions); intoRelationId = heap_create_with_catalog(intoName, namespaceId, tablespaceId, @@ -800,10 +807,8 @@ InitPlan(QueryDesc *queryDesc, int eflags) true, 0, parseTree->intoOnCommit, - allowSystemTableMods, - options); - if (options) - pfree(options); + reloptions, + allowSystemTableMods); FreeTupleDesc(tupdesc); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 8ca24c17aa..f818e4a332 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.341 2006/07/02 05:17:26 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.342 2006/07/03 22:45:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1680,7 +1680,6 @@ _copyQuery(Query *from) COPY_NODE_FIELD(utilityStmt); COPY_SCALAR_FIELD(resultRelation); COPY_NODE_FIELD(into); - COPY_SCALAR_FIELD(intoHasOids); COPY_NODE_FIELD(intoOptions); COPY_SCALAR_FIELD(intoOnCommit); COPY_STRING_FIELD(intoTableSpaceName); @@ -2641,7 +2640,6 @@ _copyExecuteStmt(ExecuteStmt *from) COPY_STRING_FIELD(name); COPY_NODE_FIELD(into); - COPY_SCALAR_FIELD(into_has_oids); COPY_NODE_FIELD(intoOptions); COPY_SCALAR_FIELD(into_on_commit); COPY_STRING_FIELD(into_tbl_space); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 424e546b4b..b753fb148c 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.275 2006/07/02 05:17:26 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.276 2006/07/03 22:45:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -658,7 +658,6 @@ _equalQuery(Query *a, Query *b) COMPARE_NODE_FIELD(utilityStmt); COMPARE_SCALAR_FIELD(resultRelation); COMPARE_NODE_FIELD(into); - COMPARE_SCALAR_FIELD(intoHasOids); COMPARE_NODE_FIELD(intoOptions); COMPARE_SCALAR_FIELD(intoOnCommit); COMPARE_STRING_FIELD(intoTableSpaceName); @@ -1474,7 +1473,6 @@ _equalExecuteStmt(ExecuteStmt *a, ExecuteStmt *b) { COMPARE_STRING_FIELD(name); COMPARE_NODE_FIELD(into); - COMPARE_SCALAR_FIELD(into_has_oids); COMPARE_NODE_FIELD(intoOptions); COMPARE_SCALAR_FIELD(into_on_commit); COMPARE_STRING_FIELD(into_tbl_space); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index d865360563..b51127fba9 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.276 2006/07/02 02:23:20 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.277 2006/07/03 22:45:39 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -1509,7 +1509,6 @@ _outQuery(StringInfo str, Query *node) WRITE_INT_FIELD(resultRelation); WRITE_NODE_FIELD(into); - WRITE_BOOL_FIELD(intoHasOids); WRITE_NODE_FIELD(intoOptions); WRITE_ENUM_FIELD(intoOnCommit, OnCommitAction); WRITE_STRING_FIELD(intoTableSpaceName); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 0e061c44c5..883155dcd0 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.190 2006/07/02 02:23:20 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.191 2006/07/03 22:45:39 tgl Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -140,7 +140,6 @@ _readQuery(void) READ_NODE_FIELD(utilityStmt); READ_INT_FIELD(resultRelation); READ_NODE_FIELD(into); - READ_BOOL_FIELD(intoHasOids); READ_NODE_FIELD(intoOptions); READ_ENUM_FIELD(intoOnCommit, OnCommitAction); READ_STRING_FIELD(intoTableSpaceName); diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 9e66ca1ab6..0d1adeac18 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.337 2006/07/02 02:23:20 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.338 2006/07/03 22:45:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1882,7 +1882,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) if (stmt->intoColNames) applyColumnNames(qry->targetList, stmt->intoColNames); - qry->intoHasOids = interpretOidsOption(stmt->intoOptions); qry->intoOptions = copyObject(stmt->intoOptions); qry->intoOnCommit = stmt->intoOnCommit; qry->intoTableSpaceName = stmt->intoTableSpaceName; @@ -2754,8 +2753,6 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt) paramtypes = FetchPreparedStatementParams(stmt->name); - stmt->into_has_oids = interpretOidsOption(stmt->intoOptions); - if (stmt->params || paramtypes) { int nparams = list_length(stmt->params); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index f09a7a6b2d..754777c57b 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.550 2006/07/02 02:23:21 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.551 2006/07/03 22:45:39 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -232,7 +232,7 @@ static void doNegateFloat(Value *v); func_as createfunc_opt_list alterfunc_opt_list aggr_args aggr_args_list old_aggr_definition old_aggr_list oper_argtypes RuleActionList RuleActionMulti - opt_column_list columnList opt_name_list + opt_column_list columnList opt_name_list sort_clause opt_sort_clause sortby_list index_params name_list from_clause from_list opt_array_bounds qualified_name_list any_name any_name_list @@ -1562,25 +1562,15 @@ alter_rel_cmd: | SET definition { AlterTableCmd *n = makeNode(AlterTableCmd); - n->subtype = AT_SetOptions; + n->subtype = AT_SetRelOptions; n->def = (Node *)$2; $$ = (Node *)n; } + /* ALTER [TABLE|INDEX] <name> RESET (...) */ | RESET definition { - AlterTableCmd *n; - ListCell *cell; - - foreach(cell, $2) - { - if (((DefElem *) lfirst(cell))->arg != NULL) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("parameters for RESET should not take values"))); - } - - n = makeNode(AlterTableCmd); - n->subtype = AT_SetOptions; + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ResetRelOptions; n->def = (Node *)$2; $$ = (Node *)n; } @@ -1919,7 +1909,7 @@ ColConstraintElem: n->indexspace = NULL; $$ = (Node *)n; } - | UNIQUE OptConsTableSpace + | UNIQUE opt_definition OptConsTableSpace { Constraint *n = makeNode(Constraint); n->contype = CONSTR_UNIQUE; @@ -1927,7 +1917,8 @@ ColConstraintElem: n->raw_expr = NULL; n->cooked_expr = NULL; n->keys = NULL; - n->indexspace = $2; + n->options = $2; + n->indexspace = $3; $$ = (Node *)n; } | PRIMARY KEY opt_definition OptConsTableSpace @@ -2100,7 +2091,7 @@ ConstraintElem: n->indexspace = NULL; $$ = (Node *)n; } - | UNIQUE '(' columnList ')' OptConsTableSpace + | UNIQUE '(' columnList ')' opt_definition OptConsTableSpace { Constraint *n = makeNode(Constraint); n->contype = CONSTR_UNIQUE; @@ -2108,7 +2099,8 @@ ConstraintElem: n->raw_expr = NULL; n->cooked_expr = NULL; n->keys = $3; - n->indexspace = $5; + n->options = $5; + n->indexspace = $6; $$ = (Node *)n; } | PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace @@ -2214,13 +2206,12 @@ OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; } | /*EMPTY*/ { $$ = NIL; } ; +/* WITH (options) is preferred, WITH OIDS and WITHOUT OIDS are legacy forms */ OptWith: - WITH OIDS { $$ = list_make1(defWithOids(true)); } - | WITHOUT OIDS { $$ = list_make1(defWithOids(false)); } - | WITH definition { $$ = $2; } - | WITH OIDS WITH definition { $$ = lappend($4, defWithOids(true)); } - | WITHOUT OIDS WITH definition { $$ = lappend($4, defWithOids(false)); } - | /*EMPTY*/ { $$ = NIL; } + WITH definition { $$ = $2; } + | WITH OIDS { $$ = list_make1(defWithOids(true)); } + | WITHOUT OIDS { $$ = list_make1(defWithOids(false)); } + | /*EMPTY*/ { $$ = NIL; } ; OnCommitOption: ON COMMIT DROP { $$ = ONCOMMIT_DROP; } @@ -2874,6 +2865,8 @@ def_elem: ColLabel '=' def_arg /* Note: any simple identifier will be returned as a type name! */ def_arg: func_type { $$ = (Node *)$1; } + | func_name_keyword { $$ = (Node *)makeString(pstrdup($1)); } + | reserved_keyword { $$ = (Node *)makeString(pstrdup($1)); } | qual_all_Op { $$ = (Node *)$1; } | NumericOnly { $$ = (Node *)$1; } | Sconst { $$ = (Node *)makeString($1); } diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index a4fe1999fe..b83780408e 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.150 2006/07/02 02:23:21 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.151 2006/07/03 22:45:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,7 +34,6 @@ #include "rewrite/rewriteManip.h" #include "utils/builtins.h" #include "utils/guc.h" -#include "utils/memutils.h" #define ORDER_CLAUSE 0 @@ -66,8 +65,6 @@ static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, Var *l_colvar, Var *r_colvar); static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause); -static bool OptionMatches(text *t, const char* kw, char **str, Size *len); -static Datum OptionToText(DefElem *def); /* @@ -216,18 +213,18 @@ interpretInhOption(InhOption inhOpt) } /* - * Given a List that indicates whether WITH / WITHOUT OIDS was - * specified by the user, return true iff the specified table/result - * set should be created with OIDs. This needs to be done after - * parsing the query string because the return value can depend upon - * the default_with_oids GUC var. + * Given a relation-options list (of DefElems), return true iff the specified + * table/result set should be created with OIDs. This needs to be done after + * parsing the query string because the return value can depend upon the + * default_with_oids GUC var. */ bool -interpretOidsOption(List *options) +interpretOidsOption(List *defList) { ListCell *cell; - foreach(cell, options) + /* Scan list to see if OIDS was included */ + foreach(cell, defList) { DefElem *def = (DefElem *) lfirst(cell); @@ -235,264 +232,11 @@ interpretOidsOption(List *options) return defGetBoolean(def); } - /* oids option is not specified. */ + /* OIDS option was not specified, so use default. */ return default_with_oids; } /* - * Test if t is start with 'kw='. - */ -static bool -OptionMatches(text *t, const char* kw, char **str, Size *len) -{ - char *text_str = (char *) VARATT_DATA(t); - int text_len = VARATT_SIZE(t) - VARHDRSZ; - Size kwlen = strlen(kw); - - if (text_len > kwlen && text_str[kwlen] == '=' && - pg_strncasecmp(text_str, kw, kwlen) == 0) - { - *str = text_str + kwlen + 1; - *len = text_len - kwlen - 1; - return true; - } - - return false; -} - -/* - * Flatten a DefElem to a text like as 'defname=arg'. - */ -static Datum -OptionToText(DefElem *def) -{ - text *t; - char *value = defGetString(def); - Size len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value); - - t = palloc(len + 1); - VARATT_SIZEP(t) = len; - sprintf((char *) VARATT_DATA(t), "%s=%s", def->defname, value); - - return PointerGetDatum(t); -} - -/* - * Merge option array and option list. - * - * array Existing option, or NULL if new option. - * list List of DefElems to be added to array. - */ -ArrayType * -OptionBuild(ArrayType *array, List *list) -{ - ListCell *cell; - bool *used; - int index; - int o; - ArrayType *result; - ArrayBuildState *astate; - MemoryContext myContext; - MemoryContext oldContext; - - if (list_length(list) == 0) - { - /* no additinal elements, so just clone. */ - if (array == NULL) - return NULL; - result = palloc(VARATT_SIZE(array)); - memcpy(result, array, VARATT_SIZE(array)); - return result; - } - - /* Make a temporary context to hold all the junk */ - myContext = AllocSetContextCreate(CurrentMemoryContext, - "OptionBuild", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - oldContext = MemoryContextSwitchTo(myContext); - - astate = NULL; - used = (bool *) palloc0(sizeof(bool) * list_length(list)); - - if (array) - { - Assert(ARR_ELEMTYPE(array) == TEXTOID); - Assert(ARR_NDIM(array) == 1); - Assert(ARR_LBOUND(array)[0] == 1); - - for (o = 1; o <= ARR_DIMS(array)[0]; o++) - { - bool isnull; - Datum datum; - - datum = array_ref(array, 1, &o, - -1 /* varlenarray */ , - -1 /* TEXT's typlen */ , - false /* TEXT's typbyval */ , - 'i' /* TEXT's typalign */ , - &isnull); - if (isnull) - continue; - - index = 0; - foreach(cell, list) - { - DefElem *def = lfirst(cell); - - /* - * We ignore 'oids' item because it is stored - * in pg_class.relhasoids. - */ - if (!used[index] && - pg_strcasecmp(def->defname, "oids") != 0) - { - char *value_str; - Size value_len; - if (OptionMatches(DatumGetTextP(datum), - def->defname, &value_str, &value_len)) - { - used[index] = true; - if (def->arg) - { - /* Replace an existing option. */ - datum = OptionToText(def); - goto next; /* skip remain items in list */ - } - else - { - /* Remove the option from array. */ - goto skip; - } - } - } - - index++; - } - - /* - * The datum is an existing parameter and is not modified. - * Fall down. - */ - -next: - astate = accumArrayResult(astate, datum, false, TEXTOID, myContext); -skip: - ; - } - } - - /* - * add options not in array - */ - index = 0; - foreach(cell, list) - { - DefElem *def = lfirst(cell); - - if (!used[index] && def->arg && - pg_strcasecmp(def->defname, "oids") != 0) - { - astate = accumArrayResult(astate, OptionToText(def), - false, TEXTOID, myContext); - } - - index++; - } - - if (astate) - result = DatumGetArrayTypeP(makeArrayResult(astate, oldContext)); - else - result = NULL; - - MemoryContextSwitchTo(oldContext); - MemoryContextDelete(myContext); - return result; -} - -/* - * Support routine to parse options. - * - * options List of DefElems - * num length of kwds - * kwds supported keywords - * strict Throw error if unsupported option is found. - * - * FIXME: memory is leaked in kwds[].arg. - */ -void -OptionParse(ArrayType *options, Size num, DefElem kwds[], bool strict) -{ - Size k; - int o; - - for (k = 0; k < num; k++) - { - Assert(kwds[k].defname); - kwds[k].arg = NULL; - } - - if (options == NULL) - return; /* use default for all */ - - Assert(ARR_ELEMTYPE(options) == TEXTOID); - Assert(ARR_NDIM(options) == 1); - Assert(ARR_LBOUND(options)[0] == 1); - - for (o = 1; o <= ARR_DIMS(options)[0]; o++) - { - bool isnull; - Datum datum; - - datum = array_ref(options, 1, &o, - -1 /* varlenarray */ , - -1 /* TEXT's typlen */ , - false /* TEXT's typbyval */ , - 'i' /* TEXT's typalign */ , - &isnull); - if (isnull) - continue; - - for (k = 0; k < num; k++) - { - char *value_str; - Size value_len; - - if (OptionMatches(DatumGetTextP(datum), - kwds[k].defname, &value_str, &value_len)) - { - char *value; - - if (kwds[k].arg != NULL) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("duplicated parameter %s", - kwds[k].defname))); - - /* copy value as Value node */ - value = (char *) palloc(value_len + 1); - strncpy(value, value_str, value_len); - value[value_len] = '\0'; - kwds[k].arg = (Node *) makeString(value); - goto next; /* skip remain keywords */ - } - } - - /* parameter is not in kwds */ - if (strict) - { - char *c = DatumGetCString(DirectFunctionCall1(textout, datum)); - - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unsupported parameter %s", c))); - } -next:; - } -} - -/* * Extract all not-in-common columns from column lists of a source table */ static void diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 2ddbb64763..8e51b40d71 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2,7 +2,7 @@ * ruleutils.c - Functions to convert stored expressions/querytrees * back to source text * - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.225 2006/07/02 02:23:21 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.226 2006/07/03 22:45:39 tgl Exp $ **********************************************************************/ #include "postgres.h" @@ -757,20 +757,23 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags) } if (!colno) + { appendStringInfoChar(&buf, ')'); - /* - * If it has options, append "WITH (options)" - */ - str = flatten_reloptions(indexrelid); - if (str) - { - appendStringInfo(&buf, " WITH (%s)", str); - pfree(str); - } + /* + * If it has options, append "WITH (options)" + */ + str = flatten_reloptions(indexrelid); + if (str) + { + appendStringInfo(&buf, " WITH (%s)", str); + pfree(str); + } + + /* + * XXX we don't include the tablespace ... this is for pg_dump + */ - if (!colno) - { /* * If it's a partial index, decompile and append the predicate */ @@ -1020,6 +1023,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, if (fullCommand && OidIsValid(conForm->conrelid)) { char *options = flatten_reloptions(conForm->conrelid); + if (options) { appendStringInfo(&buf, " WITH (%s)", options); @@ -4937,35 +4941,42 @@ string_to_text(char *str) return result; } +/* + * Generate a C string representing a relation's reloptions, or NULL if none. + */ static char * flatten_reloptions(Oid relid) { - HeapTuple tuple; char *result = NULL; + HeapTuple tuple; + Datum reloptions; + bool isnull; tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0); - if (tuple) + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relid); + + reloptions = SysCacheGetAttr(RELOID, tuple, + Anum_pg_class_reloptions, &isnull); + if (!isnull) { - bool isnull; - Datum reloptions; - reloptions = SysCacheGetAttr(RELOID, tuple, - Anum_pg_class_reloptions, &isnull); - if (!isnull) - { - Datum sep, - txt; - sep = DirectFunctionCall1(textin, CStringGetDatum(", ")); - /* - * OID 395 = array_to_text. - * DirectFunctionCall2(array_to_text) is not available here. - */ - txt = OidFunctionCall2(395, reloptions, sep); - result = DatumGetCString(DirectFunctionCall1(textout, txt)); - } - ReleaseSysCache(tuple); + Datum sep, + txt; + + /* + * We want to use array_to_text(reloptions, ', ') --- but + * DirectFunctionCall2(array_to_text) does not work, because + * array_to_text() relies on flinfo to be valid. So use + * OidFunctionCall2. + */ + sep = DirectFunctionCall1(textin, CStringGetDatum(", ")); + txt = OidFunctionCall2(F_ARRAY_TO_TEXT, reloptions, sep); + result = DatumGetCString(DirectFunctionCall1(textout, txt)); } + ReleaseSysCache(tuple); + return result; } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 50edf7691a..a806080fd0 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.243 2006/07/02 02:23:21 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.244 2006/07/03 22:45:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,6 +32,7 @@ #include "access/genam.h" #include "access/heapam.h" +#include "access/reloptions.h" #include "catalog/catalog.h" #include "catalog/indexing.h" #include "catalog/namespace.h" @@ -47,8 +48,6 @@ #include "catalog/pg_proc.h" #include "catalog/pg_rewrite.h" #include "catalog/pg_type.h" -#include "catalog/heap.h" -#include "catalog/index.h" #include "commands/trigger.h" #include "miscadmin.h" #include "optimizer/clauses.h" @@ -56,7 +55,6 @@ #include "optimizer/prep.h" #include "storage/fd.h" #include "storage/smgr.h" -#include "utils/array.h" #include "utils/builtins.h" #include "utils/catcache.h" #include "utils/fmgroids.h" @@ -186,17 +184,19 @@ static void RelationClearRelation(Relation relation, bool rebuild); static void RelationReloadClassinfo(Relation relation); static void RelationFlushRelation(Relation relation); static bool load_relcache_init_file(void); -static void write_item(const void *data, Size len, FILE *fp); static void write_relcache_init_file(void); +static void write_item(const void *data, Size len, FILE *fp); static void formrdesc(const char *relationName, Oid relationReltype, bool hasoids, int natts, FormData_pg_attribute *att); static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK); static Relation AllocateRelationDesc(Relation relation, Form_pg_class relp); +static void RelationParseRelOptions(Relation relation, HeapTuple tuple); static void RelationBuildTupleDesc(Relation relation); static Relation RelationBuildDesc(Oid targetRelId, Relation oldrelation); static void RelationInitPhysicalAddr(Relation relation); +static TupleDesc GetPgClassDescriptor(void); static TupleDesc GetPgIndexDescriptor(void); static void AttrDefaultFetch(Relation relation); static void CheckConstraintFetch(Relation relation); @@ -210,7 +210,6 @@ static void IndexSupportInitialize(oidvector *indclass, static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid, StrategyNumber numStrats, StrategyNumber numSupport); -static void RelationParseOptions(Relation relation, HeapTuple tuple); /* @@ -303,10 +302,13 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp) * Copy the relation tuple form * * We only allocate space for the fixed fields, ie, CLASS_TUPLE_SIZE. - * relacl is NOT stored in the relcache --- there'd be little point in it, - * since we don't copy the tuple's nullvalues bitmap and hence wouldn't - * know if the value is valid ... bottom line is that relacl *cannot* be - * retrieved from the relcache. Get it from the syscache if you need it. + * The variable-length fields (relacl, reloptions) are NOT stored in the + * relcache --- there'd be little point in it, since we don't copy the + * tuple's nulls bitmap and hence wouldn't know if the values are valid. + * Bottom line is that relacl *cannot* be retrieved from the relcache. + * Get it from the syscache if you need it. The same goes for the + * original form of reloptions (however, we do store the parsed form + * of reloptions in rd_options). */ relationForm = (Form_pg_class) palloc(CLASS_TUPLE_SIZE); @@ -314,7 +316,6 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp) /* initialize relation tuple form */ relation->rd_rel = relationForm; - relation->rd_options = NULL; /* and allocate attribute tuple form storage */ relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, @@ -328,49 +329,71 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp) } /* - * RelationParseOptions + * RelationParseRelOptions + * Convert pg_class.reloptions into pre-parsed rd_options + * + * tuple is the real pg_class tuple (not rd_rel!) for relation + * + * Note: rd_rel and (if an index) rd_am must be valid already */ static void -RelationParseOptions(Relation relation, HeapTuple tuple) +RelationParseRelOptions(Relation relation, HeapTuple tuple) { - ArrayType *options; + Datum datum; + bool isnull; + bytea *options; - Assert(tuple); + relation->rd_options = NULL; + /* Fall out if relkind should not have options */ switch (relation->rd_rel->relkind) { - case RELKIND_RELATION: - case RELKIND_TOASTVALUE: - case RELKIND_UNCATALOGED: - case RELKIND_INDEX: - break; - default: - /* other relation should not have options. */ - relation->rd_options = NULL; - return; + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_UNCATALOGED: + case RELKIND_INDEX: + break; + default: + return; } - /* SysCacheGetAttr is not available here. */ - if (heap_attisnull(tuple, Anum_pg_class_reloptions)) - options = NULL; - else - options = (ArrayType *) ((Form_pg_class) GETSTRUCT(tuple))->reloptions; + /* + * Fetch reloptions from tuple; have to use a hardwired descriptor + * because we might not have any other for pg_class yet (consider + * executing this code for pg_class itself) + */ + datum = fastgetattr(tuple, + Anum_pg_class_reloptions, + GetPgClassDescriptor(), + &isnull); + if (isnull) + return; + /* Parse into appropriate format; don't error out here */ switch (relation->rd_rel->relkind) { - case RELKIND_RELATION: - case RELKIND_TOASTVALUE: - case RELKIND_UNCATALOGED: - relation->rd_options = heap_option( - relation->rd_rel->relkind, options); - break; - case RELKIND_INDEX: - relation->rd_options = index_option( - relation->rd_am->amoption, options); - break; - default: - /* should not happen */ - break; + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_UNCATALOGED: + options = heap_reloptions(relation->rd_rel->relkind, datum, + false); + break; + case RELKIND_INDEX: + options = index_reloptions(relation->rd_am->amoptions, datum, + false); + break; + default: + Assert(false); /* can't get here */ + options = NULL; /* keep compiler quiet */ + break; + } + + /* Copy parsed data into CacheMemoryContext */ + if (options) + { + relation->rd_options = MemoryContextAlloc(CacheMemoryContext, + VARSIZE(options)); + memcpy(relation->rd_options, options, VARSIZE(options)); } } @@ -820,6 +843,9 @@ RelationBuildDesc(Oid targetRelId, Relation oldrelation) if (OidIsValid(relation->rd_rel->relam)) RelationInitIndexAccessInfo(relation); + /* extract reloptions if any */ + RelationParseRelOptions(relation, pg_class_tuple); + /* * initialize the relation lock manager information */ @@ -833,9 +859,6 @@ RelationBuildDesc(Oid targetRelId, Relation oldrelation) /* make sure relation is marked as having no open file yet */ relation->rd_smgr = NULL; - /* Build AM-specific fields. */ - RelationParseOptions(relation, pg_class_tuple); - /* * now we can free the memory allocated for pg_class_tuple */ @@ -1266,7 +1289,6 @@ formrdesc(const char *relationName, Oid relationReltype, * data from pg_class and replace what we've done here. */ relation->rd_rel = (Form_pg_class) palloc0(CLASS_TUPLE_SIZE); - relation->rd_options = NULL; namestrcpy(&relation->rd_rel->relname, relationName); relation->rd_rel->relnamespace = PG_CATALOG_NAMESPACE; @@ -1355,11 +1377,6 @@ formrdesc(const char *relationName, Oid relationReltype, } /* - * initialize the rd_options field to default value - */ - relation->rd_options = heap_option(RELKIND_RELATION, NULL); - - /* * add new reldesc to relcache */ RelationCacheInsert(relation); @@ -1537,9 +1554,11 @@ RelationReloadClassinfo(Relation relation) RelationGetRelid(relation)); relp = (Form_pg_class) GETSTRUCT(pg_class_tuple); memcpy(relation->rd_rel, relp, CLASS_TUPLE_SIZE); + /* Reload reloptions in case they changed */ if (relation->rd_options) pfree(relation->rd_options); - RelationParseOptions(relation, pg_class_tuple); + RelationParseRelOptions(relation, pg_class_tuple); + /* done with pg_class tuple */ heap_freetuple(pg_class_tuple); /* We must recalculate physical address in case it changed */ RelationInitPhysicalAddr(relation); @@ -2178,7 +2197,6 @@ RelationBuildLocalRelation(const char *relname, * initialize relation tuple form (caller may add/override data later) */ rel->rd_rel = (Form_pg_class) palloc0(CLASS_TUPLE_SIZE); - rel->rd_options = NULL; namestrcpy(&rel->rd_rel->relname, relname); rel->rd_rel->relnamespace = relnamespace; @@ -2412,6 +2430,11 @@ RelationCacheInitializePhase2(void) Assert(relation->rd_rel != NULL); memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE); + /* Update rd_options while we have the tuple */ + if (relation->rd_options) + pfree(relation->rd_options); + RelationParseRelOptions(relation, htup); + /* * Also update the derived fields in rd_att. */ @@ -2450,49 +2473,72 @@ RelationCacheInitializePhase2(void) } /* + * GetPgClassDescriptor -- get a predefined tuple descriptor for pg_class * GetPgIndexDescriptor -- get a predefined tuple descriptor for pg_index * * We need this kluge because we have to be able to access non-fixed-width - * fields of pg_index before we have the standard catalog caches available. - * We use predefined data that's set up in just the same way as the - * bootstrapped reldescs used by formrdesc(). The resulting tupdesc is - * not 100% kosher: it does not have the correct rowtype OID in tdtypeid, - * nor does it have a TupleConstr field. But it's good enough for the - * purpose of extracting fields. + * fields of pg_class and pg_index before we have the standard catalog caches + * available. We use predefined data that's set up in just the same way as + * the bootstrapped reldescs used by formrdesc(). The resulting tupdesc is + * not 100% kosher: it does not have the correct rowtype OID in tdtypeid, nor + * does it have a TupleConstr field. But it's good enough for the purpose of + * extracting fields. */ static TupleDesc -GetPgIndexDescriptor(void) +BuildHardcodedDescriptor(int natts, Form_pg_attribute attrs, bool hasoids) { - static TupleDesc pgindexdesc = NULL; + TupleDesc result; MemoryContext oldcxt; int i; - /* Already done? */ - if (pgindexdesc) - return pgindexdesc; - oldcxt = MemoryContextSwitchTo(CacheMemoryContext); - pgindexdesc = CreateTemplateTupleDesc(Natts_pg_index, false); - pgindexdesc->tdtypeid = RECORDOID; /* not right, but we don't care */ - pgindexdesc->tdtypmod = -1; + result = CreateTemplateTupleDesc(natts, hasoids); + result->tdtypeid = RECORDOID; /* not right, but we don't care */ + result->tdtypmod = -1; - for (i = 0; i < Natts_pg_index; i++) + for (i = 0; i < natts; i++) { - memcpy(pgindexdesc->attrs[i], - &Desc_pg_index[i], - ATTRIBUTE_TUPLE_SIZE); + memcpy(result->attrs[i], &attrs[i], ATTRIBUTE_TUPLE_SIZE); /* make sure attcacheoff is valid */ - pgindexdesc->attrs[i]->attcacheoff = -1; + result->attrs[i]->attcacheoff = -1; } /* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */ - pgindexdesc->attrs[0]->attcacheoff = 0; + result->attrs[0]->attcacheoff = 0; /* Note: we don't bother to set up a TupleConstr entry */ MemoryContextSwitchTo(oldcxt); + return result; +} + +static TupleDesc +GetPgClassDescriptor(void) +{ + static TupleDesc pgclassdesc = NULL; + + /* Already done? */ + if (pgclassdesc == NULL) + pgclassdesc = BuildHardcodedDescriptor(Natts_pg_class, + Desc_pg_class, + true); + + return pgclassdesc; +} + +static TupleDesc +GetPgIndexDescriptor(void) +{ + static TupleDesc pgindexdesc = NULL; + + /* Already done? */ + if (pgindexdesc == NULL) + pgindexdesc = BuildHardcodedDescriptor(Natts_pg_index, + Desc_pg_index, + false); + return pgindexdesc; } @@ -3109,7 +3155,7 @@ load_relcache_init_file(void) if ((nread = fread(rel->rd_options, 1, len, fp)) != len) goto read_failed; if (len != VARATT_SIZE(rel->rd_options)) - goto read_failed; + goto read_failed; /* sanity check */ } else { @@ -3299,15 +3345,6 @@ read_failed: return false; } -static void -write_item(const void *data, Size len, FILE *fp) -{ - if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len)) - elog(FATAL, "could not write init file"); - if (fwrite(data, 1, len, fp) != len) - elog(FATAL, "could not write init file"); -} - /* * Write out a new initialization file with the current contents * of the relcache. @@ -3380,13 +3417,13 @@ write_relcache_init_file(void) /* next, do all the attribute tuple form data entries */ for (i = 0; i < relform->relnatts; i++) { - write_item(rel->rd_att->attrs[i], - ATTRIBUTE_TUPLE_SIZE, fp); + write_item(rel->rd_att->attrs[i], ATTRIBUTE_TUPLE_SIZE, fp); } /* next, do the access method specific field */ write_item(rel->rd_options, - (rel->rd_options ? VARATT_SIZE(rel->rd_options) : 0), fp); + (rel->rd_options ? VARATT_SIZE(rel->rd_options) : 0), + fp); /* If it's an index, there's more to do */ if (rel->rd_rel->relkind == RELKIND_INDEX) @@ -3396,18 +3433,21 @@ write_relcache_init_file(void) /* write the pg_index tuple */ /* we assume this was created by heap_copytuple! */ write_item(rel->rd_indextuple, - HEAPTUPLESIZE + rel->rd_indextuple->t_len, fp); + HEAPTUPLESIZE + rel->rd_indextuple->t_len, + fp); /* next, write the access method tuple form */ write_item(am, sizeof(FormData_pg_am), fp); /* next, write the vector of operator OIDs */ - write_item(rel->rd_operator, relform->relnatts * - (am->amstrategies * sizeof(Oid)), fp); + write_item(rel->rd_operator, + relform->relnatts * (am->amstrategies * sizeof(Oid)), + fp); /* finally, write the vector of support procedures */ - write_item(rel->rd_support, relform->relnatts * - (am->amsupport * sizeof(RegProcedure)), fp); + write_item(rel->rd_support, + relform->relnatts * (am->amsupport * sizeof(RegProcedure)), + fp); } /* also make a list of their OIDs, for RelationIdIsInInitFile */ @@ -3463,6 +3503,16 @@ write_relcache_init_file(void) LWLockRelease(RelCacheInitLock); } +/* write a chunk of data preceded by its length */ +static void +write_item(const void *data, Size len, FILE *fp) +{ + if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len)) + elog(FATAL, "could not write init file"); + if (fwrite(data, 1, len, fp) != len) + elog(FATAL, "could not write init file"); +} + /* * Detect whether a given relation (identified by OID) is one of the ones * we store in the init file. |