diff options
57 files changed, 1133 insertions, 1093 deletions
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 7aeb9f8b64b..cd3789f4ed2 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.124 2006/06/03 02:53:04 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.125 2006/07/03 22:45:36 tgl Exp $ --> <!-- Documentation of the system catalogs, directed toward PostgreSQL developers --> @@ -499,6 +499,13 @@ <entry>Function to estimate cost of an index scan</entry> </row> + <row> + <entry><structfield>amoptions</structfield></entry> + <entry><type>regproc</type></entry> + <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry> + <entry>Function to parse and validate reloptions for an index</entry> + </row> + </tbody> </tgroup> </table> @@ -1643,6 +1650,15 @@ for details </entry> </row> + + <row> + <entry><structfield>reloptions</structfield></entry> + <entry><type>text[]</type></entry> + <entry></entry> + <entry> + Access-method-specific options, as <quote>keyword=value</> strings + </entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml index a001cd4e33a..7febd0c9072 100644 --- a/doc/src/sgml/indexam.sgml +++ b/doc/src/sgml/indexam.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.14 2006/06/06 17:59:57 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.15 2006/07/03 22:45:36 tgl Exp $ --> <chapter id="indexam"> <title>Index Access Method Interface Definition</title> @@ -234,6 +234,47 @@ amvacuumcleanup (IndexVacuumInfo *info, </para> <para> +<programlisting> +void +amcostestimate (PlannerInfo *root, + IndexOptInfo *index, + List *indexQuals, + RelOptInfo *outer_rel, + Cost *indexStartupCost, + Cost *indexTotalCost, + Selectivity *indexSelectivity, + double *indexCorrelation); +</programlisting> + Estimate the costs of an index scan. This function is described fully + in <xref linkend="index-cost-estimation">, below. + </para> + + <para> +<programlisting> +bytea * +amoptions (ArrayType *reloptions, + bool validate); +</programlisting> + Parse and validate the reloptions array for an index. This is called only + when a non-null reloptions array exists for the index. + <parameter>reloptions</> is a <type>text</> array containing entries of the + form <replaceable>name</><literal>=</><replaceable>value</>. + The function should construct a <type>bytea</> value, which will be copied + into the <structfield>rd_options</> field of the index's relcache entry. + The data contents of the <type>bytea</> value are open for the access + method to define, but the standard access methods currently all use struct + <structname>StdRdOptions</>. + When <parameter>validate</> is true, the function should report a suitable + error message if any of the options are unrecognized or have invalid + values; when <parameter>validate</> is false, invalid entries should be + silently ignored. (<parameter>validate</> is false when loading options + already stored in <structname>pg_catalog</>; an invalid entry could only + be found if the access method has changed its rules for options, and in + that case ignoring obsolete entries is appropriate.) + It is OK to return NULL if default behavior is wanted. + </para> + + <para> The purpose of an index, of course, is to support scans for tuples matching an indexable <literal>WHERE</> condition, often called a <firstterm>qualifier</> or <firstterm>scan key</>. The semantics of @@ -339,28 +380,16 @@ amrestrpos (IndexScanDesc scan); </para> <para> -<programlisting> -void -amcostestimate (PlannerInfo *root, - IndexOptInfo *index, - List *indexQuals, - RelOptInfo *outer_rel, - Cost *indexStartupCost, - Cost *indexTotalCost, - Selectivity *indexSelectivity, - double *indexCorrelation); -</programlisting> - Estimate the costs of an index scan. This function is described fully - in <xref linkend="index-cost-estimation">, below. - </para> - - <para> - By convention, the <literal>pg_proc</literal> entry for any index + By convention, the <literal>pg_proc</literal> entry for an index access method function should show the correct number of arguments, but declare them all as type <type>internal</> (since most of the arguments have types that are not known to SQL, and we don't want users calling the functions directly anyway). The return type is declared as <type>void</>, <type>internal</>, or <type>boolean</> as appropriate. + The only exception is <function>amoptions</>, which should be correctly + declared as taking <type>text[]</> and <type>bool</> and returning + <type>bytea</>. This provision allows client code to execute + <function>amoptions</> to test validity of options settings. </para> </sect1> diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile index 7cd4a4ab0e7..103bae2f5b5 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 edd90c8a56f..70e8fd948dd 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 00000000000..8506070f101 --- /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 17e041bc4a8..544956c84ef 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 4137ab4426f..bc915332b51 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 ae1fbc73201..33cdae37c3e 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 bed0b5dbd72..f5a1fcfd814 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 fbee1fdc2aa..a1a03ddcf36 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 cf3344c2009..cf4b920f7da 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 82fb0a3268a..aaaec863ba1 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 cfc2a833cd3..399386c8567 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 3b78843101e..a1768886915 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 05785d98eb5..1dd3be0a63d 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 93a9b0df653..6750a982d7c 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 da30e39b42d..b6fd45d6dfb 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 901c392d7e0..9bb8f01b1e4 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 e2eed023e02..3a6bc46a696 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 9143f6068a5..f047f58b01f 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 2ed1a156a4c..5724467d70a 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 08b76e96d5f..fa41de3a2dc 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 ebde8f10950..149e1b6dae0 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 7e35258ac09..d2c4bf89059 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 27ccb3fb879..78ec950f235 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 65712c2092c..cae956c22d9 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 7b2a0508feb..f67f1b62ec4 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 24a93e998de..17a802dc30e 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 8e97fa47563..264cb437786 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 bb03b9358c6..3c9f0798ba4 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 8ca24c17aae..f818e4a332a 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 424e546b4b8..b753fb148c2 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 d8653605635..b51127fba96 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 0e061c44c55..883155dcd07 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 9e66ca1ab65..0d1adeac18c 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 f09a7a6b2de..754777c57bc 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 a4fe1999fe5..b83780408e5 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 2ddbb64763c..8e51b40d712 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 50edf7691a4..a806080fd01 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. diff --git a/src/include/access/genam.h b/src/include/access/genam.h index c7ebae99315..9a8828a33ac 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -7,14 +7,13 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.61 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.62 2006/07/03 22:45:39 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef GENAM_H #define GENAM_H -#include "access/heapam.h" #include "access/itup.h" #include "access/relscan.h" #include "access/sdir.h" @@ -145,13 +144,4 @@ extern SysScanDesc systable_beginscan(Relation heapRelation, extern HeapTuple systable_getnext(SysScanDesc sysscan); extern void systable_endscan(SysScanDesc sysscan); - -typedef HeapOption IndexOption; - -extern bytea *genam_option(ArrayType *options, - int minFillfactor, int defaultFillfactor); - -#define IndexGetFillFactor(relation) HeapGetFillFactor(relation) -#define IndexGetPageFreeSpace(relation) HeapGetPageFreeSpace(relation) - #endif /* GENAM_H */ diff --git a/src/include/access/gin.h b/src/include/access/gin.h index 4749eca192a..de3100c4cae 100644 --- a/src/include/access/gin.h +++ b/src/include/access/gin.h @@ -3,7 +3,7 @@ * header file for postgres inverted index access method implementation. * * Copyright (c) 2006, PostgreSQL Global Development Group - * $PostgreSQL: pgsql/src/include/access/gin.h,v 1.2 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/gin.h,v 1.3 2006/07/03 22:45:39 tgl Exp $ *-------------------------------------------------------------------------- */ @@ -213,7 +213,7 @@ typedef struct ginxlogDeletePage { } ginxlogDeletePage; /* ginutil.c */ -extern Datum ginoption(PG_FUNCTION_ARGS); +extern Datum ginoptions(PG_FUNCTION_ARGS); extern void initGinState( GinState *state, Relation index ); extern Buffer GinNewBuffer(Relation index); extern void GinInitBuffer(Buffer b, uint32 f); diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index 5be99712b00..bf9dd11e215 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/gist_private.h,v 1.19 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/gist_private.h,v 1.20 2006/07/03 22:45:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -272,7 +272,10 @@ extern Datum gistgetmulti(PG_FUNCTION_ARGS); #define GiSTPageSize \ ( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) ) -extern Datum gistoption(PG_FUNCTION_ARGS); +#define GIST_MIN_FILLFACTOR 50 +#define GIST_DEFAULT_FILLFACTOR 90 + +extern Datum gistoptions(PG_FUNCTION_ARGS); extern bool gistfitpage(IndexTuple *itvec, int len); extern bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace); extern void gistcheckpage(Relation rel, Buffer buf); @@ -330,4 +333,5 @@ extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, GistEntryVector *entryvec, int attno); + #endif /* GIST_PRIVATE_H */ diff --git a/src/include/access/hash.h b/src/include/access/hash.h index 59cf5c9961a..10646522d93 100644 --- a/src/include/access/hash.h +++ b/src/include/access/hash.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/hash.h,v 1.70 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/hash.h,v 1.71 2006/07/03 22:45:39 tgl Exp $ * * NOTES * modeled after Margo Seltzer's hash implementation for unix. @@ -167,6 +167,9 @@ typedef HashMetaPageData *HashMetaPage; MAXALIGN(sizeof(HashPageOpaqueData)) - \ sizeof(ItemIdData)) +#define HASH_MIN_FILLFACTOR 50 +#define HASH_DEFAULT_FILLFACTOR 75 + /* * Constants */ @@ -234,7 +237,7 @@ extern Datum hashmarkpos(PG_FUNCTION_ARGS); extern Datum hashrestrpos(PG_FUNCTION_ARGS); extern Datum hashbulkdelete(PG_FUNCTION_ARGS); extern Datum hashvacuumcleanup(PG_FUNCTION_ARGS); -extern Datum hashoption(PG_FUNCTION_ARGS); +extern Datum hashoptions(PG_FUNCTION_ARGS); /* * Datatype-specific hash functions in hashfunc.c. diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 085501be7d2..a2ca20bddd9 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.113 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.114 2006/07/03 22:45:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,7 +22,6 @@ #include "nodes/primnodes.h" #include "storage/block.h" #include "storage/lmgr.h" -#include "utils/array.h" #include "utils/rel.h" #include "utils/tqual.h" @@ -228,32 +227,4 @@ extern MinimalTuple minimal_tuple_from_heap_tuple(HeapTuple htup); extern HeapTuple heap_addheader(int natts, bool withoid, Size structlen, void *structure); -extern HeapTuple build_class_tuple(Form_pg_class pgclass, ArrayType *options); - -/* - * HeapOption - * Internal data of heaps. - */ -typedef struct HeapOption -{ - int32 vl_len; - int fillfactor; -} HeapOption; - -extern bytea *heap_option(char relkind, ArrayType *options); - -/* - * HeapGetFillFactor - * Returns the heap's fillfactor. - */ -#define HeapGetFillFactor(relation) \ - (((HeapOption*)(relation)->rd_options)->fillfactor) - -/* - * HeapGetPageFreeSpace - * Returns the heap's freespace per page in bytes. - */ -#define HeapGetPageFreeSpace(relation) \ - (BLCKSZ * (100 - HeapGetFillFactor(relation)) / 100) - #endif /* HEAPAM_H */ diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h index e5981d092c0..43bd49a7bb5 100644 --- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/nbtree.h,v 1.99 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/nbtree.h,v 1.100 2006/07/03 22:45:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -107,6 +107,13 @@ typedef struct BTMetaPageData MAXALIGN(sizeof(BTPageOpaqueData))) / 3 - sizeof(ItemIdData)) /* + * Because of above, min fillfactor can't be less than 2/3rds; see notes in + * nbtsort.c before you change these! + */ +#define BTREE_MIN_FILLFACTOR 70 +#define BTREE_DEFAULT_FILLFACTOR 90 + +/* * Test whether two btree entries are "the same". * * Old comments: @@ -453,7 +460,7 @@ extern Datum btmarkpos(PG_FUNCTION_ARGS); extern Datum btrestrpos(PG_FUNCTION_ARGS); extern Datum btbulkdelete(PG_FUNCTION_ARGS); extern Datum btvacuumcleanup(PG_FUNCTION_ARGS); -extern Datum btoption(PG_FUNCTION_ARGS); +extern Datum btoptions(PG_FUNCTION_ARGS); /* * prototypes for functions in nbtinsert.c diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h new file mode 100644 index 00000000000..82474f4707a --- /dev/null +++ b/src/include/access/reloptions.h @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------- + * + * reloptions.h + * Core support for relation options (pg_class.reloptions) + * + * Note: the functions dealing with text-array reloptions values declare + * them as Datum, not ArrayType *, to avoid needing to include array.h + * into a lot of low-level code. + * + * + * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL: pgsql/src/include/access/reloptions.h,v 1.1 2006/07/03 22:45:40 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef RELOPTIONS_H +#define RELOPTIONS_H + +#include "nodes/pg_list.h" + +extern Datum transformRelOptions(Datum oldOptions, List *defList, + bool ignoreOids, bool isReset); + +extern void parseRelOptions(Datum options, int numkeywords, + const char * const *keywords, + char **values, bool validate); + +extern bytea *default_reloptions(Datum reloptions, bool validate, + int minFillfactor, int defaultFillfactor); + +extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate); + +extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions, + bool validate); + +#endif /* RELOPTIONS_H */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 10e71559bc1..59dea36fe42 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.335 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.336 2006/07/03 22:45:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200607011 +#define CATALOG_VERSION_NO 200607021 #endif diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index 23604ec2ee1..09aa05f44c2 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.83 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.84 2006/07/03 22:45:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,6 @@ #include "catalog/pg_attribute.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" -#include "utils/array.h" #include "utils/rel.h" @@ -55,8 +54,8 @@ extern Oid 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); extern void heap_drop_with_catalog(Oid relid); @@ -66,6 +65,11 @@ extern void heap_truncate_check_FKs(List *relations, bool tempTables); extern List *heap_truncate_find_FKs(List *relationIds); +extern void InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, + Datum reloptions); + extern List *AddRelationRawConstraints(Relation rel, List *rawColDefaults, List *rawConstraints); diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index f4b1bc98304..c1af45da8d1 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.67 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.68 2006/07/03 22:45:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,6 @@ #include "access/itup.h" #include "catalog/pg_index.h" #include "nodes/execnodes.h" -#include "utils/array.h" #define DEFAULT_INDEX_TYPE "btree" @@ -38,7 +37,7 @@ extern Oid index_create(Oid heapRelationId, Oid accessMethodObjectId, Oid tableSpaceId, Oid *classObjectId, - List *options, + Datum reloptions, bool isprimary, bool istoast, bool isconstraint, @@ -72,6 +71,4 @@ extern double IndexBuildHeapScan(Relation heapRelation, extern void reindex_index(Oid indexId); extern bool reindex_relation(Oid relid, bool toast_too); -extern bytea *index_option(RegProcedure amoption, ArrayType *options); - #endif /* INDEX_H */ diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h index 5e0d1cf4b6e..048e3e97f7a 100644 --- a/src/include/catalog/pg_am.h +++ b/src/include/catalog/pg_am.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.44 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.45 2006/07/03 22:45:40 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -65,7 +65,7 @@ CATALOG(pg_am,2601) regproc ambulkdelete; /* bulk-delete function */ regproc amvacuumcleanup; /* post-VACUUM cleanup function */ regproc amcostestimate; /* estimate cost of an indexscan */ - regproc amoption; /* parse AM-specific parameters */ + regproc amoptions; /* parse AM-specific parameters */ } FormData_pg_am; /* ---------------- @@ -103,23 +103,23 @@ typedef FormData_pg_am *Form_pg_am; #define Anum_pg_am_ambulkdelete 21 #define Anum_pg_am_amvacuumcleanup 22 #define Anum_pg_am_amcostestimate 23 -#define Anum_pg_am_amoption 24 +#define Anum_pg_am_amoptions 24 /* ---------------- * initial contents of pg_am * ---------------- */ -DATA(insert OID = 403 ( btree 5 1 1 t t t t f t t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoption )); +DATA(insert OID = 403 ( btree 5 1 1 t t t t f t t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions )); DESCR("b-tree index access method"); #define BTREE_AM_OID 403 -DATA(insert OID = 405 ( hash 1 1 0 f f f f f t f hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoption )); +DATA(insert OID = 405 ( hash 1 1 0 f f f f f t f hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions )); DESCR("hash index access method"); #define HASH_AM_OID 405 -DATA(insert OID = 783 ( gist 100 7 0 f t t t t t t gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoption )); +DATA(insert OID = 783 ( gist 100 7 0 f t t t t t t gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions )); DESCR("GiST index access method"); #define GIST_AM_OID 783 -DATA(insert OID = 2742 ( gin 100 4 0 f f f f t t f gininsert ginbeginscan gingettuple gingetmulti ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoption )); +DATA(insert OID = 2742 ( gin 100 4 0 f f f f t t f gininsert ginbeginscan gingettuple gingetmulti ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions )); DESCR("GIN index access method"); #define GIN_AM_OID 2742 diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h index c0be03ac4d3..fe4a7189bae 100644 --- a/src/include/catalog/pg_attribute.h +++ b/src/include/catalog/pg_attribute.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.121 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.122 2006/07/03 22:45:40 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -404,8 +404,8 @@ DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); { 1259, {"relhaspkey"}, 16, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ { 1259, {"relhasrules"}, 16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ { 1259, {"relhassubclass"},16, -1, 1, 24, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \ -{ 1259, {"reloptions"}, 1009, -1, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \ -{ 1259, {"relacl"}, 1034, -1, -1, 26, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 } +{ 1259, {"relacl"}, 1034, -1, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \ +{ 1259, {"reloptions"}, 1009, -1, -1, 26, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 } DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p i t f f t 0)); DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0)); @@ -431,8 +431,8 @@ DATA(insert ( 1259 relhasoids 16 -1 1 21 0 -1 -1 t p c t f f t 0)); DATA(insert ( 1259 relhaspkey 16 -1 1 22 0 -1 -1 t p c t f f t 0)); DATA(insert ( 1259 relhasrules 16 -1 1 23 0 -1 -1 t p c t f f t 0)); DATA(insert ( 1259 relhassubclass 16 -1 1 24 0 -1 -1 t p c t f f t 0)); -DATA(insert ( 1259 reloptions 1009 -1 -1 25 1 -1 -1 f x i f f f t 0)); -DATA(insert ( 1259 relacl 1034 -1 -1 26 1 -1 -1 f x i f f f t 0)); +DATA(insert ( 1259 relacl 1034 -1 -1 25 1 -1 -1 f x i f f f t 0)); +DATA(insert ( 1259 reloptions 1009 -1 -1 26 1 -1 -1 f x i f f f t 0)); DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0)); DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0)); DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0)); diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 32b3406aa87..6d303b1cd66 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.93 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.94 2006/07/03 22:45:40 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -31,15 +31,6 @@ * typedef struct FormData_pg_class * ---------------- */ - -/* ---------------- - * This structure is actually variable-length (the last attribute is - * a POSTGRES array). Hence, sizeof(FormData_pg_class) does not - * necessarily match the actual length of the structure. Furthermore - * relacl may be a NULL field. Hence, you MUST use heap_getattr() - * to get the relacl field ... and don't forget to check isNull. - * ---------------- - */ #define RelationRelationId 1259 CATALOG(pg_class,1259) BKI_BOOTSTRAP @@ -75,12 +66,17 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP bool relhasrules; /* has associated rules */ bool relhassubclass; /* has derived classes */ - /* following fields may or may not be present, see note above! */ - text reloptions[1]; /* access method specific data */ - aclitem relacl[1]; /* we declare this just for the catalog */ + /* + * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too. + * + * NOTE: these fields are not present in a relcache entry's rd_rel field. + */ + + aclitem relacl[1]; /* access permissions */ + text reloptions[1]; /* access-method-specific options */ } FormData_pg_class; -/* Size of fixed part of pg_class tuples, not counting relacl or padding */ +/* Size of fixed part of pg_class tuples, not counting var-length fields */ #define CLASS_TUPLE_SIZE \ (offsetof(FormData_pg_class,relhassubclass) + sizeof(bool)) @@ -96,13 +92,6 @@ typedef FormData_pg_class *Form_pg_class; * ---------------- */ -/* ---------------- - * Natts_pg_class_fixed is used to tell routines that insert new - * pg_class tuples (as opposed to replacing old ones) that there's no - * relacl field. This is a kluge. - * ---------------- - */ -#define Natts_pg_class_fixed 24 #define Natts_pg_class 26 #define Anum_pg_class_relname 1 #define Anum_pg_class_relnamespace 2 @@ -128,8 +117,8 @@ typedef FormData_pg_class *Form_pg_class; #define Anum_pg_class_relhaspkey 22 #define Anum_pg_class_relhasrules 23 #define Anum_pg_class_relhassubclass 24 -#define Anum_pg_class_reloptions 25 -#define Anum_pg_class_relacl 26 +#define Anum_pg_class_relacl 25 +#define Anum_pg_class_reloptions 26 /* ---------------- * initial contents of pg_class diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index aba5eee5fef..efa54141536 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.414 2006/07/02 02:23:22 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.415 2006/07/03 22:45:40 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -680,7 +680,7 @@ DATA(insert OID = 972 ( btvacuumcleanup PGNSP PGUID 12 f f t f v 2 2281 "2281 DESCR("btree(internal)"); DATA(insert OID = 1268 ( btcostestimate PGNSP PGUID 12 f f t f v 8 2278 "2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ btcostestimate - _null_ )); DESCR("btree(internal)"); -DATA(insert OID = 2785 ( btoption PGNSP PGUID 12 f f t f v 1 2281 "2281" _null_ _null_ _null_ btoption - _null_ )); +DATA(insert OID = 2785 ( btoptions PGNSP PGUID 12 f f t f s 2 17 "1009 16" _null_ _null_ _null_ btoptions - _null_ )); DESCR("btree(internal)"); DATA(insert OID = 339 ( poly_same PGNSP PGUID 12 f f t f i 2 16 "604 604" _null_ _null_ _null_ poly_same - _null_ )); @@ -799,7 +799,7 @@ DATA(insert OID = 425 ( hashvacuumcleanup PGNSP PGUID 12 f f t f v 2 2281 "2281 DESCR("hash(internal)"); DATA(insert OID = 438 ( hashcostestimate PGNSP PGUID 12 f f t f v 8 2278 "2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ hashcostestimate - _null_ )); DESCR("hash(internal)"); -DATA(insert OID = 2786 ( hashoption PGNSP PGUID 12 f f t f v 1 2281 "2281" _null_ _null_ _null_ hashoption - _null_ )); +DATA(insert OID = 2786 ( hashoptions PGNSP PGUID 12 f f t f s 2 17 "1009 16" _null_ _null_ _null_ hashoptions - _null_ )); DESCR("hash(internal)"); DATA(insert OID = 449 ( hashint2 PGNSP PGUID 12 f f t f i 1 23 "21" _null_ _null_ _null_ hashint2 - _null_ )); @@ -1067,7 +1067,7 @@ DATA(insert OID = 2561 ( gistvacuumcleanup PGNSP PGUID 12 f f t f v 2 2281 "2 DESCR("gist(internal)"); DATA(insert OID = 772 ( gistcostestimate PGNSP PGUID 12 f f t f v 8 2278 "2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ gistcostestimate - _null_ )); DESCR("gist(internal)"); -DATA(insert OID = 2787 ( gistoption PGNSP PGUID 12 f f t f v 1 2281 "2281" _null_ _null_ _null_ gistoption - _null_ )); +DATA(insert OID = 2787 ( gistoptions PGNSP PGUID 12 f f t f s 2 17 "1009 16" _null_ _null_ _null_ gistoptions - _null_ )); DESCR("gist(internal)"); DATA(insert OID = 784 ( tintervaleq PGNSP PGUID 12 f f t f i 2 16 "704 704" _null_ _null_ _null_ tintervaleq - _null_ )); @@ -3855,7 +3855,7 @@ DATA(insert OID = 2740 ( ginvacuumcleanup PGNSP PGUID 12 f f t f v 2 2281 "2281 DESCR("gin(internal)"); DATA(insert OID = 2741 ( gincostestimate PGNSP PGUID 12 f f t f v 8 2278 "2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ gincostestimate - _null_ )); DESCR("gin(internal)"); -DATA(insert OID = 2788 ( ginoption PGNSP PGUID 12 f f t f v 1 2281 "2281" _null_ _null_ _null_ ginoption - _null_ )); +DATA(insert OID = 2788 ( ginoptions PGNSP PGUID 12 f f t f s 2 17 "1009 16" _null_ _null_ _null_ ginoptions - _null_ )); DESCR("gin(internal)"); /* GIN array support */ diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index e0678db4cf1..ac4b2a3a501 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.73 2006/07/02 02:23:23 momjian Exp $ + * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.74 2006/07/03 22:45:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -95,7 +95,6 @@ extern int64 defGetInt64(DefElem *def); extern List *defGetQualifiedName(DefElem *def); extern TypeName *defGetTypeName(DefElem *def); extern int defGetTypeLength(DefElem *def); - extern DefElem *defWithOids(bool value); #endif /* DEFREM_H */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 6cda429d3e6..fa63ca185bb 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.314 2006/07/02 02:23:23 momjian Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.315 2006/07/03 22:45:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -92,9 +92,8 @@ typedef struct Query int resultRelation; /* target relation (index into rtable) */ RangeVar *into; /* target relation for SELECT INTO */ - bool intoHasOids; /* should target relation contain OIDs? */ - List *intoOptions; /* options passed by WITH */ - OnCommitAction intoOnCommit; /* what do we do at COMMIT? */ + List *intoOptions; /* options from WITH clause */ + OnCommitAction intoOnCommit; /* what do we do at COMMIT? */ char *intoTableSpaceName; /* table space to use, or NULL */ bool hasAggs; /* has aggregates in tlist or havingQual */ @@ -708,7 +707,7 @@ typedef struct SelectStmt * lcons(NIL,NIL) for all (SELECT DISTINCT) */ RangeVar *into; /* target table (for select into table) */ List *intoColNames; /* column names for into table */ - List *intoOptions; /* options passed by WITH */ + List *intoOptions; /* options from WITH clause */ OnCommitAction intoOnCommit; /* what do we do at COMMIT? */ char *intoTableSpaceName; /* table space to use, or NULL */ List *targetList; /* the target list (of ResTarget) */ @@ -861,7 +860,8 @@ typedef enum AlterTableType AT_DropCluster, /* SET WITHOUT CLUSTER */ AT_DropOids, /* SET WITHOUT OIDS */ AT_SetTableSpace, /* SET TABLESPACE */ - AT_SetOptions, /* SET (...) -- AM specific parameters */ + AT_SetRelOptions, /* SET (...) -- AM specific parameters */ + AT_ResetRelOptions, /* RESET (...) -- AM specific parameters */ AT_EnableTrig, /* ENABLE TRIGGER name */ AT_DisableTrig, /* DISABLE TRIGGER name */ AT_EnableTrigAll, /* ENABLE TRIGGER ALL */ @@ -1017,7 +1017,7 @@ typedef struct CreateStmt List *inhRelations; /* relations to inherit from (list of * inhRelation) */ List *constraints; /* constraints (list of Constraint nodes) */ - List *options; /* options passed by WITH */ + List *options; /* options from WITH clause */ OnCommitAction oncommit; /* what do we do at COMMIT? */ char *tablespacename; /* table space to use, or NULL */ } CreateStmt; @@ -1075,7 +1075,7 @@ typedef struct Constraint Node *raw_expr; /* expr, as untransformed parse tree */ char *cooked_expr; /* expr, as nodeToString representation */ List *keys; /* String nodes naming referenced column(s) */ - List *options; /* options passed by WITH */ + List *options; /* options from WITH clause */ char *indexspace; /* index tablespace for PKEY/UNIQUE * constraints; NULL for default */ } Constraint; @@ -1429,7 +1429,7 @@ typedef struct IndexStmt char *accessMethod; /* name of access method (eg. btree) */ char *tableSpace; /* tablespace, or NULL to use parent's */ List *indexParams; /* a list of IndexElem */ - List *options; /* options passed by WITH */ + List *options; /* options from WITH clause */ Node *whereClause; /* qualification (partial-index predicate) */ List *rangetable; /* range table for qual and/or expressions, * filled in by transformStmt() */ @@ -1886,8 +1886,7 @@ typedef struct ExecuteStmt NodeTag type; char *name; /* The name of the plan to execute */ RangeVar *into; /* Optional table to store results in */ - bool into_has_oids; /* Merge GUC info with user input */ - List *intoOptions; /* options passed by WITH */ + List *intoOptions; /* Options from WITH clause */ OnCommitAction into_on_commit; /* What do we do at COMMIT? */ char *into_tbl_space; /* Tablespace to use, or NULL */ List *params; /* Values to assign to parameters */ diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h index dc04bcb0bb8..c328a1bd5aa 100644 --- a/src/include/parser/parse_clause.h +++ b/src/include/parser/parse_clause.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/parser/parse_clause.h,v 1.45 2006/07/02 02:23:23 momjian Exp $ + * $PostgreSQL: pgsql/src/include/parser/parse_clause.h,v 1.46 2006/07/03 22:45:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,17 +15,12 @@ #define PARSE_CLAUSE_H #include "parser/parse_node.h" -#include "utils/array.h" extern void transformFromClause(ParseState *pstate, List *frmList); extern int setTargetTable(ParseState *pstate, RangeVar *relation, bool inh, bool alsoSource, AclMode requiredPerms); extern bool interpretInhOption(InhOption inhOpt); -extern bool interpretOidsOption(List *options); - -extern ArrayType *OptionBuild(ArrayType *array, List *list); -extern void OptionParse(ArrayType *options, Size num, DefElem kwds[], - bool strict); +extern bool interpretOidsOption(List *defList); extern Node *transformWhereClause(ParseState *pstate, Node *clause, const char *constructName); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 9d2e4a8bee8..7bb1b270a49 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.90 2006/07/02 02:23:23 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.91 2006/07/03 22:45:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -115,7 +115,7 @@ typedef struct RelationAmInfo FmgrInfo ambulkdelete; FmgrInfo amvacuumcleanup; FmgrInfo amcostestimate; - FmgrInfo amoption; + FmgrInfo amoptions; } RelationAmInfo; @@ -143,14 +143,8 @@ typedef struct RelationData * survived into; or zero if the rel was not created in the current top * transaction. This should be relied on only for optimization purposes; * it is possible for new-ness to be "forgotten" (eg, after CLUSTER). - * - * rd_options and rd_amcache are alike, but different in terms of - * lifetime. Invalidation of rd_options is at the change of pg_class - * and of rd_amcache is at the change of AM's metapages. Also, rd_options - * is serialized in the relcache init file, but rd_amcache is not. */ Form_pg_class rd_rel; /* RELATION tuple */ - bytea *rd_options; /* parsed rd_rel->reloptions */ TupleDesc rd_att; /* tuple descriptor */ Oid rd_id; /* relation's object id */ List *rd_indexlist; /* list of OIDs of indexes on relation */ @@ -160,6 +154,13 @@ typedef struct RelationData MemoryContext rd_rulescxt; /* private memory cxt for rd_rules, if any */ TriggerDesc *trigdesc; /* Trigger info, or NULL if rel has none */ + /* + * rd_options is set whenever rd_rel is loaded into the relcache entry. + * Note that you can NOT look into rd_rel for this data. NULL means + * "use defaults". + */ + bytea *rd_options; /* parsed pg_class.reloptions */ + /* These are non-NULL only for an index relation: */ Form_pg_index rd_index; /* pg_index tuple describing this index */ struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */ @@ -208,6 +209,45 @@ typedef Relation *RelationPtr; /* + * StdRdOptions + * Standard contents of rd_options for heaps and generic indexes. + * + * RelationGetFillFactor() and RelationGetTargetPageFreeSpace() can only + * be applied to relations that use this format or a superset for + * private options data. + */ +typedef struct StdRdOptions +{ + int32 vl_len; /* required to be a bytea */ + int fillfactor; /* page fill factor in percent (0..100) */ +} StdRdOptions; + +#define HEAP_MIN_FILLFACTOR 10 +#define HEAP_DEFAULT_FILLFACTOR 100 + +/* + * RelationGetFillFactor + * Returns the relation's fillfactor. Note multiple eval of argument! + */ +#define RelationGetFillFactor(relation, defaultff) \ + ((relation)->rd_options ? \ + ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff)) + +/* + * RelationGetTargetPageUsage + * Returns the relation's desired space usage per page in bytes. + */ +#define RelationGetTargetPageUsage(relation, defaultff) \ + (BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100) + +/* + * RelationGetTargetPageFreeSpace + * Returns the relation's desired freespace per page in bytes. + */ +#define RelationGetTargetPageFreeSpace(relation, defaultff) \ + (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100) + +/* * RelationIsValid * True iff relation descriptor is valid. */ |