pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feed From: Nikita Glukhov <n.gluhov@postgrespro.ru>
To: Alvaro Herrera <alvherre@2ndquadrant.com>
To: Tomas Vondra <tomas.vondra@2ndquadrant.com>
To: pgsql-hackers@postgresql.org <pgsql-hackers@postgresql.org>
Cc: Robert Haas <robertmhaas@gmail.com>
Cc: Tom Lane <tgl@sss.pgh.pa.us>
Cc: Nikolay Shaplov <dhyan@nataraj.su>
Cc: Oleg Bartunov <obartunov@gmail.com>
Subject: Re: [PATCH] Opclass parameters
Date: Tue, 10 Sep 2019 04:30:41 +0300
Message-ID: <dcb740af-2a93-8785-eb26-a4273029f2fb@postgrespro.ru> (raw )
In-Reply-To: <20190903220254.GA22389@alvherre.pgsql >
References: <20190903220254.GA22389@alvherre.pgsql >
On 04.09.2019 1:02, Alvaro Herrera wrote:
> On 2019-Jun-11, Tomas Vondra wrote:
>
>> 1) We need a better infrastructure to parse opclass parameters. For
>> example the gtsvector_options does this:
> I think this is part of what Nikolay's patch series was supposed to
> address. But that one has been going way too slow. I agree we need
> something better.
API was simplified in the new version of the patches (see below).
>> 2) The 0001 part does this in index_opclass_options_generic:
>>
>> get_opclass_name(opclass, InvalidOid, &str);
>>
>> ereport(ERROR,
>> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
>> errmsg("operator class \"%s\" has no options",
>> opclassname.data)));
>>
>> But that's a bit broken, because get_opclass_name() appends the opclass
>> name to 'str', but with a space at the beginning.
> Yeah, I think just exporting get_opclass_name from ruleutils.c is a bad
> idea. Sounds like we need a (very small) new function in lsyscache.c
> that does the job of extracting the opclass name, and then the ruleutils
> function can call that one to avoid duplicated code.
I decided to add new function generate_opclass_name() like existing
generate_collation_name(), and to reuse static get_opclass_name().
> Anyway, this patchset doesn't apply anymore. Somebody (maybe its
> author this time?) please rebase.
New version of rebased patches is attached:
1. Opclass parameters infrastructure.
API was completely refactored since the previous version:
- API was generalized for all AMs. Previously, each AM should implement
opclass options parsing/passing in its own way using its own support
function numbers.
Now each AMs uses 0th support function (discussable). Binary bytea values
of parsed options are passed to support functions using special expression
node initialized in FmgrInfo.fn_expr (see macro PG_GET_OPCLASS_OPTIONS(),
get_fn_opclass_options(), set_fn_opclass_options).
- Introduced new structure local_relopts (needs a better name, of course)
with a set of functions for opclass/AM options definition. The parsing
was moved into index_opclass_option(). That was necessary for mixing of
AM- and opclass-specific options. Opclasses now extend the structure with
AM's options adding their own options. See patch #4 for an example.
2. New AM method amattoptions().
amattoptions() is used to specify per-column AM-specific options.
The example is signature length for bloom indexes (patch #3).
3. Use amattoptions() in contrib/bloom.
This is an attempt to replace "col1"-"col32" AM reloptions with
per-column option "bits" using new amattoptions API:
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits = 3), t);
4. An example of using new API in GiST tsvector_ops.
Opclass options definition looks much simpler now:
typedef struct GistTsVectorOptions
{
/* GistAttOptions am_att_options; (future) */
int siglen;
} GistTsVectorOptions;
Datum
gtsvector_options(PG_FUNCTION_ARGS)
{
local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
GistTsVectorOptions *options = NULL;
extend_local_reloptions(relopts, options, sizeof(*options));
add_local_int_reloption(relopts, "siglen", "signature length",
SIGLEN_DEFAULT, 1, SIGLEN_MAX,
&options->siglen);
PG_RETURN_VOID();
}
5. Remove pg_index.indoption column (experimental).
pg_index.indoption (array of per-column ASC/DESC, NULLS FIRST/LAST
flags) can be removed now, and these flags can be stored as
special per-column AM-specific options "desc" and "nulls_first".
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
[text/x-patch] 0001-Introduce-opclass-parameters-20190910.patch (83.2K, ../dcb740af-2a93-8785-eb26-a4273029f2fb@postgrespro.ru/3-0001-Introduce-opclass-parameters-20190910.patch)
download | inline diff:
From 3a9b74780956951bdbe60cdaa96e9752675d3a34 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 1/5] Introduce opclass parameters
---
contrib/bloom/blvalidate.c | 3 +
doc/src/sgml/indices.sgml | 2 +-
doc/src/sgml/ref/create_index.sgml | 16 +-
src/backend/access/brin/brin_validate.c | 3 +
src/backend/access/common/reloptions.c | 410 +++++++++++++++++++----
src/backend/access/gin/ginvalidate.c | 3 +
src/backend/access/gist/gistvalidate.c | 3 +
src/backend/access/hash/hashvalidate.c | 2 +
src/backend/access/index/indexam.c | 74 +++-
src/backend/access/nbtree/nbtvalidate.c | 3 +
src/backend/access/spgist/spgvalidate.c | 3 +
src/backend/catalog/heap.c | 8 +-
src/backend/catalog/index.c | 22 +-
src/backend/catalog/toasting.c | 1 +
src/backend/commands/indexcmds.c | 65 +++-
src/backend/commands/opclasscmds.c | 41 ++-
src/backend/commands/tablecmds.c | 2 +-
src/backend/nodes/copyfuncs.c | 1 +
src/backend/nodes/equalfuncs.c | 1 +
src/backend/nodes/makefuncs.c | 3 +
src/backend/nodes/outfuncs.c | 1 +
src/backend/optimizer/util/plancat.c | 3 +
src/backend/parser/gram.y | 72 ++--
src/backend/parser/parse_utilcmd.c | 8 +
src/backend/utils/adt/ruleutils.c | 135 +++++---
src/backend/utils/adt/selfuncs.c | 23 +-
src/backend/utils/cache/lsyscache.c | 35 ++
src/backend/utils/cache/relcache.c | 151 ++++++++-
src/backend/utils/fmgr/fmgr.c | 28 ++
src/include/access/genam.h | 3 +
src/include/access/reloptions.h | 36 ++
src/include/catalog/heap.h | 1 +
src/include/catalog/pg_amproc.h | 2 +
src/include/fmgr.h | 4 +
src/include/nodes/execnodes.h | 2 +
src/include/nodes/parsenodes.h | 1 +
src/include/nodes/pathnodes.h | 1 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/rel.h | 1 +
src/include/utils/relcache.h | 3 +
src/include/utils/ruleutils.h | 1 +
src/test/regress/expected/alter_generic.out | 22 +-
src/test/regress/expected/btree_index.out | 5 +
src/test/regress/expected/opr_sanity.out | 2 +-
src/test/regress/input/create_function_1.source | 5 +
src/test/regress/output/create_function_1.source | 4 +
src/test/regress/regress.c | 7 +
src/test/regress/sql/alter_generic.sql | 13 +-
src/test/regress/sql/btree_index.sql | 4 +
src/test/regress/sql/opr_sanity.sql | 2 +-
50 files changed, 1050 insertions(+), 192 deletions(-)
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
index e9bd1b4..dbd675b 100644
--- a/contrib/bloom/blvalidate.c
+++ b/contrib/bloom/blvalidate.c
@@ -105,6 +105,9 @@ blvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case BLOOM_HASH_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
1, 1, opckeytype);
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 95c0a19..ea3acea 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -1253,7 +1253,7 @@ SELECT target FROM tests WHERE subject = 'some-subject' AND success;
An index definition can specify an <firstterm>operator
class</firstterm> for each column of an index.
<synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
</synopsis>
The operator class identifies the operators to be used by the index
for that column. For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 629a31e..61401f3 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
- ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+ ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
[ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
@@ -279,6 +279,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</varlistentry>
<varlistentry>
+ <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+ <listitem>
+ <para>
+ The name of an operator class parameter. See below for details.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>ASC</literal></term>
<listitem>
<para>
@@ -646,8 +655,9 @@ Indexes:
</para>
<para>
- An <firstterm>operator class</firstterm> can be specified for each
- column of an index. The operator class identifies the operators to be
+ An <firstterm>operator class</firstterm> with its optional parameters
+ can be specified for each column of an index.
+ The operator class identifies the operators to be
used by the index for that column. For example, a B-tree index on
four-byte integers would use the <literal>int4_ops</literal> class;
this operator class includes comparison functions for four-byte
diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c
index 0129338..9e6694e 100644
--- a/src/backend/access/brin/brin_validate.c
+++ b/src/backend/access/brin/brin_validate.c
@@ -87,6 +87,9 @@ brinvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case BRIN_PROCNUM_OPCINFO:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
1, 1, INTERNALOID);
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 20f4ed3..3ad1324 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -445,6 +445,7 @@ static relopt_string stringRelOpts[] =
4,
false,
gistValidateBufferingOption,
+ NULL,
"auto"
},
{
@@ -457,6 +458,7 @@ static relopt_string stringRelOpts[] =
0,
true,
validateWithCheckOption,
+ NULL,
NULL
},
/* list terminator */
@@ -615,6 +617,50 @@ add_reloption(relopt_gen *newoption)
need_initialization = true;
}
+typedef struct local_relopt
+{
+ relopt_gen *option;
+ int offset;
+} local_relopt;
+
+void
+init_local_reloptions(local_relopts *opts, void *base, Size base_size)
+{
+ opts->options = NIL;
+ opts->validators = NIL;
+ opts->base = base;
+ opts->base_size = base_size;
+}
+
+void
+extend_local_reloptions(local_relopts *opts, void *base, Size base_size)
+{
+ Assert(opts->base_size < base_size);
+ opts->base = base;
+ opts->base_size = base_size;
+}
+
+void
+register_reloptions_validator(local_relopts *opts, relopts_validator validator)
+{
+ opts->validators = lappend(opts->validators, validator);
+}
+
+/*
+ * add_local_reloption
+ * Add an already-created custom reloption to the local list.
+ */
+static void
+add_local_reloption(local_relopts *relopts, relopt_gen *newoption, void *pval)
+{
+ local_relopt *opt = palloc(sizeof(*opt));
+
+ opt->option = newoption;
+ opt->offset = (char *) pval - (char *) relopts->base;
+
+ relopts->options = lappend(relopts->options, opt);
+}
+
/*
* allocate_reloption
* Allocate a new reloption and initialize the type-agnostic fields
@@ -627,7 +673,10 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
size_t size;
relopt_gen *newoption;
- oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ if (kinds != RELOPT_KIND_LOCAL)
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ else
+ oldcxt = NULL;
switch (type)
{
@@ -659,17 +708,19 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
newoption->namelen = strlen(name);
newoption->type = type;
- MemoryContextSwitchTo(oldcxt);
+ if (oldcxt != NULL)
+ MemoryContextSwitchTo(oldcxt);
return newoption;
}
/*
- * add_bool_reloption
- * Add a new boolean reloption
+ * init_bool_reloption
+ * Allocate and initialize a new boolean reloption
*/
-void
-add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool default_val)
+static relopt_bool *
+init_bool_reloption(bits32 kinds, const char *name, const char *desc,
+ bool default_val)
{
relopt_bool *newoption;
@@ -677,16 +728,43 @@ add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool defaul
name, desc);
newoption->default_val = default_val;
+ return newoption;
+}
+
+/*
+ * add_bool_reloption
+ * Add a new boolean reloption
+ */
+void
+add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool default_val)
+{
+ relopt_bool *newoption = init_bool_reloption(kinds, name, desc, default_val);
+
add_reloption((relopt_gen *) newoption);
}
/*
- * add_int_reloption
- * Add a new integer reloption
+ * add_local_bool_reloption
+ * Add a new boolean local reloption
*/
void
-add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
- int min_val, int max_val)
+add_local_bool_reloption(local_relopts *relopts, const char *name,
+ const char *desc, bool default_val, bool *pval)
+{
+ relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL, name, desc,
+ default_val);
+
+ add_local_reloption(relopts, (relopt_gen *) newoption, pval);
+}
+
+
+/*
+ * init_real_reloption
+ * Allocate and initialize a new integer reloption
+ */
+static relopt_int *
+init_int_reloption(bits32 kinds, const char *name, const char *desc,
+ int default_val, int min_val, int max_val)
{
relopt_int *newoption;
@@ -696,16 +774,45 @@ add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_
newoption->min = min_val;
newoption->max = max_val;
+ return newoption;
+}
+
+/*
+ * add_int_reloption
+ * Add a new integer reloption
+ */
+void
+add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
+ int min_val, int max_val)
+{
+ relopt_int *newoption = init_int_reloption(kinds, name, desc, default_val,
+ min_val, max_val);
+
add_reloption((relopt_gen *) newoption);
}
/*
- * add_real_reloption
- * Add a new float reloption
+ * add_local_int_reloption
+ * Add a new local integer reloption
*/
void
-add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val,
- double min_val, double max_val)
+add_local_int_reloption(local_relopts *relopts, const char *name,
+ const char *desc, int default_val, int min_val,
+ int max_val, int *pval)
+{
+ relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL, name, desc,
+ default_val, min_val, max_val);
+
+ add_local_reloption(relopts, (relopt_gen *) newoption, pval);
+}
+
+/*
+ * init_real_reloption
+ * Allocate and initialize a new real reloption
+ */
+static relopt_real *
+init_real_reloption(bits32 kinds, const char *name, const char *desc,
+ double default_val, double min_val, double max_val)
{
relopt_real *newoption;
@@ -715,21 +822,46 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc, double defa
newoption->min = min_val;
newoption->max = max_val;
+ return newoption;
+}
+
+/*
+ * add_real_reloption
+ * Add a new float reloption
+ */
+void
+add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val,
+ double min_val, double max_val)
+{
+ relopt_real *newoption = init_real_reloption(kinds, name, desc, default_val,
+ min_val, max_val);
+
add_reloption((relopt_gen *) newoption);
}
/*
- * add_string_reloption
- * Add a new string reloption
- *
- * "validator" is an optional function pointer that can be used to test the
- * validity of the values. It must elog(ERROR) when the argument string is
- * not acceptable for the variable. Note that the default value must pass
- * the validation.
+ * add_local_real_reloption
+ * Add a new local float reloption
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val,
- validate_string_relopt validator)
+add_local_real_reloption(local_relopts *relopts, const char *name, const char *desc,
+ double default_val, double min_val, double max_val,
+ double *pval)
+{
+ relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL, name, desc,
+ default_val, min_val, max_val);
+
+ add_local_reloption(relopts, (relopt_gen *) newoption, pval);
+}
+
+/*
+ * init_string_reloption
+ * Allocate and initialize a new string reloption
+ */
+static relopt_string *
+init_string_reloption(bits32 kinds, const char *name, const char *desc,
+ const char *default_val, validate_string_relopt validator,
+ fill_string_relopt filler)
{
relopt_string *newoption;
@@ -740,10 +872,13 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha
newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
name, desc);
newoption->validate_cb = validator;
+ newoption->fill_cb = filler;
if (default_val)
{
- newoption->default_val = MemoryContextStrdup(TopMemoryContext,
- default_val);
+ if (kinds == RELOPT_KIND_LOCAL)
+ newoption->default_val = strdup(default_val);
+ else
+ newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val);
newoption->default_len = strlen(default_val);
newoption->default_isnull = false;
}
@@ -754,10 +889,46 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha
newoption->default_isnull = true;
}
+ return newoption;
+}
+
+/*
+ * add_string_reloption
+ * Add a new string reloption
+ *
+ * "validator" is an optional function pointer that can be used to test the
+ * validity of the values. It must elog(ERROR) when the argument string is
+ * not acceptable for the variable. Note that the default value must pass
+ * the validation.
+ */
+void
+add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val,
+ validate_string_relopt validator)
+{
+ relopt_string *newoption = init_string_reloption(kinds, name, desc,
+ default_val, validator, NULL);
+
add_reloption((relopt_gen *) newoption);
}
/*
+ * add_local_string_reloption
+ * Add a new local string reloption
+ */
+void
+add_local_string_reloption(local_relopts *relopts, const char *name,
+ const char *desc, const char *default_val,
+ validate_string_relopt validator,
+ fill_string_relopt filler, int *poffset)
+{
+ relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL, name,
+ desc, default_val,
+ validator, filler);
+
+ add_local_reloption(relopts, (relopt_gen *) newoption, poffset);
+}
+
+/*
* Transform a relation options list (list of DefElem) into the text array
* format that is kept in pg_class.reloptions, including only those options
* that are in the passed namespace. The output values do not include the
@@ -1051,6 +1222,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
return options;
}
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+ relopt_value *reloptions, int numoptions)
+{
+ ArrayType *array = DatumGetArrayTypeP(options);
+ Datum *optiondatums;
+ int noptions;
+ int i;
+
+ deconstruct_array(array, TEXTOID, -1, false, 'i',
+ &optiondatums, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *text_str = VARDATA(optiondatums[i]);
+ int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+ int j;
+
+ /* Search for a match in reloptions */
+ for (j = 0; j < numoptions; j++)
+ {
+ int kw_len = reloptions[j].gen->namelen;
+
+ if (text_len > kw_len && text_str[kw_len] == '=' &&
+ strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+ {
+ parse_one_reloption(&reloptions[j], text_str, text_len,
+ validate);
+ break;
+ }
+ }
+
+ if (j >= numoptions && validate)
+ {
+ char *s;
+ char *p;
+
+ s = TextDatumGetCString(optiondatums[i]);
+ p = strchr(s, '=');
+ if (p)
+ *p = '\0';
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s\"", s)));
+ }
+ }
+
+ /* It's worth avoiding memory leaks in this function */
+ pfree(optiondatums);
+
+ if (((void *) array) != DatumGetPointer(options))
+ pfree(array);
+}
+
/*
* Interpret reloptions that are given in text-array format.
*
@@ -1105,57 +1330,74 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
/* Done if no options */
if (PointerIsValid(DatumGetPointer(options)))
+ parseRelOptionsInternal(options, validate, reloptions, numoptions);
+
+ *numrelopts = numoptions;
+ return reloptions;
+}
+
+/* Parse local unregistered options. */
+relopt_value *
+parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
+{
+ int nopts = list_length(relopts->options);
+ relopt_value *values = palloc(sizeof(*values) * nopts);
+ ListCell *lc;
+ int i = 0;
+
+ foreach(lc, relopts->options)
{
- ArrayType *array = DatumGetArrayTypeP(options);
- Datum *optiondatums;
- int noptions;
+ local_relopt *opt = lfirst(lc);
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &optiondatums, NULL, &noptions);
+ values[i].gen = opt->option;
+ values[i].isset = false;
- for (i = 0; i < noptions; i++)
- {
- char *text_str = VARDATA(optiondatums[i]);
- int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
- int j;
+ i++;
+ }
- /* Search for a match in reloptions */
- for (j = 0; j < numoptions; j++)
- {
- int kw_len = reloptions[j].gen->namelen;
+ if (options != (Datum) 0)
+ parseRelOptionsInternal(options, validate, values, nopts);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
- {
- parse_one_reloption(&reloptions[j], text_str, text_len,
- validate);
- break;
- }
- }
+ return values;
+}
- if (j >= numoptions && validate)
- {
- char *s;
- char *p;
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+parseAndFillLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
+{
+ int noptions = list_length(relopts->options);
+ relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+ relopt_value *vals;
+ void *opts;
+ int i = 0;
+ ListCell *lc;
+
+ foreach(lc, relopts->options)
+ {
+ local_relopt *opt = lfirst(lc);
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- *p = '\0';
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter \"%s\"", s)));
- }
- }
+ elems[i].optname = opt->option->name;
+ elems[i].opttype = opt->option->type;
+ elems[i].offset = opt->offset;
- /* It's worth avoiding memory leaks in this function */
- pfree(optiondatums);
- if (((void *) array) != DatumGetPointer(options))
- pfree(array);
+ i++;
}
- *numrelopts = numoptions;
- return reloptions;
+ vals = parseLocalRelOptions(relopts, options, validate);
+ opts = allocateReloptStruct(relopts->base_size, vals, noptions);
+ fillRelOptions(opts, relopts->base_size, vals, noptions, validate, elems, noptions);
+
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc))(opts, vals, noptions);
+
+ if (elems)
+ pfree(elems);
+
+ return opts;
}
/*
@@ -1271,8 +1513,24 @@ allocateReloptStruct(Size base, relopt_value *options, int numoptions)
int i;
for (i = 0; i < numoptions; i++)
- if (options[i].gen->type == RELOPT_TYPE_STRING)
- size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
+ {
+ relopt_value *optval = &options[i];
+
+ if (optval->gen->type == RELOPT_TYPE_STRING)
+ {
+ relopt_string *optstr = (relopt_string *) optval->gen;
+
+ if (optstr->fill_cb)
+ {
+ const char *val = optval->isset ? optval->values.string_val :
+ optstr->default_isnull ? NULL : optstr->default_val;
+
+ size += optstr->fill_cb(val, NULL);
+ }
+ else
+ size += GET_STRING_RELOPTION_LEN(*optval) + 1;
+ }
+ }
return palloc0(size);
}
@@ -1336,7 +1594,21 @@ fillRelOptions(void *rdopts, Size basesize,
else
string_val = NULL;
- if (string_val == NULL)
+ if (optstring->fill_cb)
+ {
+ Size size =
+ optstring->fill_cb(string_val,
+ (char *) rdopts + offset);
+
+ if (size)
+ {
+ *(int *) itempos = offset;
+ offset += size;
+ }
+ else
+ *(int *) itempos = 0;
+ }
+ else if (string_val == NULL)
*(int *) itempos = 0;
else
{
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 63bd7f2..337eeec 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -106,6 +106,9 @@ ginvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case GIN_COMPARE_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
2, 2, opckeytype, opckeytype);
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index dfc1a87..ea8cd0e 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -106,6 +106,9 @@ gistvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case GIST_CONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
5, 5, INTERNALOID, opcintype,
diff --git a/src/backend/access/hash/hashvalidate.c b/src/backend/access/hash/hashvalidate.c
index 9315872..7650a30 100644
--- a/src/backend/access/hash/hashvalidate.c
+++ b/src/backend/access/hash/hashvalidate.c
@@ -105,6 +105,8 @@ hashvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ break;
case HASHSTANDARD_PROC:
case HASHEXTENDED_PROC:
if (!check_hash_func_signature(procform->amproc, procform->amprocnum,
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 2e8f53a..bbed726 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -45,17 +45,23 @@
#include "access/amapi.h"
#include "access/heapam.h"
+#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/tableam.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "catalog/index.h"
+#include "catalog/pg_amproc.h"
#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "nodes/makefuncs.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
+#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
+#include "utils/syscache.h"
/* ----------------------------------------------------------------
@@ -767,9 +773,9 @@ index_getprocid(Relation irel,
nproc = irel->rd_indam->amsupport;
- Assert(procnum > 0 && procnum <= (uint16) nproc);
+ Assert(procnum >= 0 && procnum <= (uint16) nproc);
- procindex = (nproc * (attnum - 1)) + (procnum - 1);
+ procindex = ((nproc + 1) * (attnum - 1)) + procnum;
loc = irel->rd_support;
@@ -801,9 +807,9 @@ index_getprocinfo(Relation irel,
nproc = irel->rd_indam->amsupport;
- Assert(procnum > 0 && procnum <= (uint16) nproc);
+ Assert(procnum >= 0 && procnum <= (uint16) nproc);
- procindex = (nproc * (attnum - 1)) + (procnum - 1);
+ procindex = ((nproc + 1) * (attnum - 1)) + procnum;
locinfo = irel->rd_supportinfo;
@@ -832,6 +838,17 @@ index_getprocinfo(Relation irel,
procnum, attnum, RelationGetRelationName(irel));
fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
+
+ if (procnum != OPCLASS_OPTIONS_PROC)
+ {
+ /* Initialize locinfo->fn_expr with opclass options Const */
+ bytea **attoptions = RelationGetIndexAttOptions(irel, false);
+ MemoryContext oldcxt = MemoryContextSwitchTo(irel->rd_indexcxt);
+
+ set_fn_opclass_options(locinfo, attoptions[attnum - 1]);
+
+ MemoryContextSwitchTo(oldcxt);
+ }
}
return locinfo;
@@ -911,3 +928,52 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
}
}
}
+
+/* ----------------
+ * index_opclass_options
+ *
+ * Parse opclass-specific options for index column.
+ * ----------------
+ */
+bytea *
+index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
+ bool validate)
+{
+ Oid procid = index_getprocid(indrel, attnum, 0);
+ FmgrInfo *procinfo;
+ local_relopts relopts;
+
+ if (!OidIsValid(procid))
+ {
+ Oid opclass;
+ Datum indclassDatum;
+ oidvector *indclass;
+ bool isnull;
+
+ if (!DatumGetPointer(attoptions))
+ return NULL; /* ok, no options, no procedure */
+
+ /*
+ * Report an error if the opclass's options-parsing procedure does not
+ * exist but the opclass options are specified.
+ */
+ indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple,
+ Anum_pg_index_indclass, &isnull);
+ Assert(!isnull);
+ indclass = (oidvector *) DatumGetPointer(indclassDatum);
+ opclass = indclass->values[attnum - 1];
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("operator class %s has no options",
+ generate_opclass_name(opclass))));
+ }
+
+ init_local_reloptions(&relopts, NULL, 0);
+
+ procinfo = index_getprocinfo(indrel, attnum, 0);
+
+ (void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
+
+ return parseAndFillLocalRelOptions(&relopts, attoptions, validate);
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index 0148ea7..418edc8 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -87,6 +87,9 @@ btvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case BTORDER_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, true,
2, 2, procform->amproclefttype,
diff --git a/src/backend/access/spgist/spgvalidate.c b/src/backend/access/spgist/spgvalidate.c
index 4b9fdbd..a5bb850 100644
--- a/src/backend/access/spgist/spgvalidate.c
+++ b/src/backend/access/spgist/spgvalidate.c
@@ -105,6 +105,9 @@ spgvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case SPGIST_CONFIG_PROC:
ok = check_amproc_signature(procform->amproc, VOIDOID, true,
2, 2, INTERNALOID, INTERNALOID);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index b7bcdd9..d5a4900 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -693,6 +693,7 @@ CheckAttributeType(const char *attname,
void
InsertPgAttributeTuple(Relation pg_attribute_rel,
Form_pg_attribute new_attribute,
+ Datum attoptions,
CatalogIndexState indstate)
{
Datum values[Natts_pg_attribute];
@@ -724,10 +725,11 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+ values[Anum_pg_attribute_attoptions - 1] = attoptions;
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
- nulls[Anum_pg_attribute_attoptions - 1] = true;
+ nulls[Anum_pg_attribute_attoptions - 1] = attoptions == (Datum) 0;
nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
nulls[Anum_pg_attribute_attmissingval - 1] = true;
@@ -781,7 +783,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
/* Make sure this is OK, too */
attr->attstattarget = -1;
- InsertPgAttributeTuple(rel, attr, indstate);
+ InsertPgAttributeTuple(rel, attr, (Datum) 0, indstate);
/* Add dependency info */
myself.classId = RelationRelationId;
@@ -819,7 +821,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
/* Fill in the correct relation OID in the copied tuple */
attStruct.attrelid = new_rel_oid;
- InsertPgAttributeTuple(rel, &attStruct, indstate);
+ InsertPgAttributeTuple(rel, &attStruct, (Datum) 0, indstate);
}
}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 3e1d406..cb7f0ba 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -26,6 +26,7 @@
#include "access/amapi.h"
#include "access/heapam.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/sysattr.h"
#include "access/tableam.h"
@@ -106,7 +107,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
Oid *classObjectId);
static void InitializeAttributeOids(Relation indexRelation,
int numatts, Oid indexoid);
-static void AppendAttributeTuples(Relation indexRelation, int numatts);
+static void AppendAttributeTuples(Relation indexRelation, int numatts,
+ Datum *attopts);
static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
Oid parentIndexId,
IndexInfo *indexInfo,
@@ -486,7 +488,7 @@ InitializeAttributeOids(Relation indexRelation,
* ----------------------------------------------------------------
*/
static void
-AppendAttributeTuples(Relation indexRelation, int numatts)
+AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts)
{
Relation pg_attribute;
CatalogIndexState indstate;
@@ -508,10 +510,11 @@ AppendAttributeTuples(Relation indexRelation, int numatts)
for (i = 0; i < numatts; i++)
{
Form_pg_attribute attr = TupleDescAttr(indexTupDesc, i);
+ Datum attoptions = attopts ? attopts[i] : (Datum) 0;
Assert(attr->attnum == i + 1);
- InsertPgAttributeTuple(pg_attribute, attr, indstate);
+ InsertPgAttributeTuple(pg_attribute, attr, attoptions, indstate);
}
CatalogCloseIndexes(indstate);
@@ -591,6 +594,7 @@ UpdateIndexRelation(Oid indexoid,
else
predDatum = (Datum) 0;
+
/*
* open the system catalog index relation
*/
@@ -933,7 +937,8 @@ index_create(Relation heapRelation,
/*
* append ATTRIBUTE tuples for the index
*/
- AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs);
+ AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs,
+ indexInfo->ii_OpclassOptions);
/* ----------------
* update pg_index
@@ -1146,6 +1151,13 @@ index_create(Relation heapRelation,
indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
+ /* Validate opclass-specific options */
+ if (indexInfo->ii_OpclassOptions)
+ for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
+ (void) index_opclass_options(indexRelation, i + 1,
+ indexInfo->ii_OpclassOptions[i],
+ true);
+
/*
* If this is bootstrap (initdb) time, then we don't actually fill in the
* index yet. We'll be creating more indexes and classes later, so we
@@ -2266,6 +2278,8 @@ BuildIndexInfo(Relation index)
&ii->ii_ExclusionStrats);
}
+ ii->ii_OpclassOptions = RelationGetIndexRawAttOptions(index);
+
return ii;
}
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index de6282a..7290731 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -304,6 +304,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
+ indexInfo->ii_OpclassOptions = NULL;
indexInfo->ii_Unique = true;
indexInfo->ii_ReadyForInserts = true;
indexInfo->ii_Concurrent = false;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 1477755..04148e5 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -90,6 +90,7 @@ static void RangeVarCallbackForReindexIndex(const RangeVar *relation,
static bool ReindexRelationConcurrently(Oid relationOid, int options);
static void ReindexPartitionedIndex(Relation parentIdx);
static void update_relispartition(Oid relationId, bool newval);
+static bool CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts);
/*
* callback argument type for RangeVarCallbackForReindexIndex()
@@ -268,6 +269,18 @@ CheckIndexCompatible(Oid oldId,
}
}
+ /* Any change in opclass options break compatibility. */
+ if (ret)
+ {
+ Datum *opclassOptions = RelationGetIndexRawAttOptions(irel);
+
+ ret = CompareOpclassOptions(opclassOptions,
+ indexInfo->ii_OpclassOptions, old_natts);
+
+ if (opclassOptions)
+ pfree(opclassOptions);
+ }
+
/* Any change in exclusion operator selections breaks compatibility. */
if (ret && indexInfo->ii_ExclusionOps != NULL)
{
@@ -302,6 +315,42 @@ CheckIndexCompatible(Oid oldId,
return ret;
}
+/*
+ * CompareOpclassOptions
+ *
+ * Compare per-column opclass options which are represented by arrays of text[]
+ * datums. Both elements of arrays and array themselves can be NULL.
+ */
+static bool
+CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts)
+{
+ int i;
+
+ if (!opts1 && !opts2)
+ return true;
+
+ for (i = 0; i < natts; i++)
+ {
+ Datum opt1 = opts1 ? opts1[i] : (Datum) 0;
+ Datum opt2 = opts2 ? opts2[i] : (Datum) 0;
+
+ if (opt1 == (Datum) 0)
+ {
+ if (opt2 == (Datum) 0)
+ continue;
+ else
+ return false;
+ }
+ else if (opt2 == (Datum) 0)
+ return false;
+
+ /* Compare non-NULL text[] datums. */
+ if (!DatumGetBool(DirectFunctionCall2(array_eq, opt1, opt2)))
+ return false;
+ }
+
+ return true;
+}
/*
* WaitForOlderSnapshots
@@ -1509,7 +1558,7 @@ CheckPredicate(Expr *predicate)
/*
* Compute per-index-column information, including indexed column numbers
- * or index expressions, opclasses, and indoptions. Note, all output vectors
+ * or index expressions, opclasses and their options. Note, all output vectors
* should be allocated for all columns, including "including" ones.
*/
static void
@@ -1810,6 +1859,20 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
accessMethodName)));
}
+ /* Set up the per-column opclass options (attoptions field). */
+ if (attribute->opclassopts)
+ {
+ Assert(attn < nkeycols);
+
+ if (!indexInfo->ii_OpclassOptions)
+ indexInfo->ii_OpclassOptions =
+ palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+
+ indexInfo->ii_OpclassOptions[attn] =
+ transformRelOptions((Datum) 0, attribute->opclassopts,
+ NULL, NULL, false, false);
+ }
+
attn++;
}
}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 6a1ccdeb..f2777a5 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -523,11 +523,11 @@ DefineOpClass(CreateOpClassStmt *stmt)
addFamilyMember(&operators, member, false);
break;
case OPCLASS_ITEM_FUNCTION:
- if (item->number <= 0 || item->number > maxProcNumber)
+ if (item->number < 0 || item->number > maxProcNumber)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("invalid function number %d,"
- " must be between 1 and %d",
+ " must be between 0 and %d",
item->number, maxProcNumber)));
funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
#ifdef NOT_USED
@@ -537,7 +537,6 @@ DefineOpClass(CreateOpClassStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
get_func_name(funcOid));
#endif
-
/* Save the info */
member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
member->object = funcOid;
@@ -902,11 +901,11 @@ AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
addFamilyMember(&operators, member, false);
break;
case OPCLASS_ITEM_FUNCTION:
- if (item->number <= 0 || item->number > maxProcNumber)
+ if (item->number < 0 || item->number > maxProcNumber)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("invalid function number %d,"
- " must be between 1 and %d",
+ " must be between 0 and %d",
item->number, maxProcNumber)));
funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
#ifdef NOT_USED
@@ -1141,6 +1140,36 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
elog(ERROR, "cache lookup failed for function %u", member->object);
procform = (Form_pg_proc) GETSTRUCT(proctup);
+ /* Check the signature of the opclass options parsing function */
+ if (member->number == OPCLASS_OPTIONS_PROC)
+ {
+ if (OidIsValid(typeoid))
+ {
+ if ((OidIsValid(member->lefttype) && member->lefttype != typeoid) ||
+ (OidIsValid(member->righttype) && member->righttype != typeoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("associated data types for opclass options "
+ "parsing functions must match opclass input type")));
+ }
+ else
+ {
+ if (member->lefttype != member->righttype)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("left and right associated data types for "
+ "opclass options parsing functions must match")));
+ }
+
+ if (procform->prorettype != VOIDOID ||
+ procform->pronargs != 1 ||
+ procform->proargtypes.values[0] != INTERNALOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("invalid opclass options parsing function"),
+ errhint("opclass options parsing function must have signature '%s'",
+ "(internal) RETURNS void")));
+ }
/*
* btree comparison procs must be 2-arg procs returning int4. btree
* sortsupport procs must take internal and return void. btree in_range
@@ -1148,7 +1177,7 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
* a 1-arg proc returning int4, while proc 2 must be a 2-arg proc
* returning int8. Otherwise we don't know.
*/
- if (amoid == BTREE_AM_OID)
+ else if (amoid == BTREE_AM_OID)
{
if (member->number == BTORDER_PROC)
{
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 05593f3..afef6a9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5737,7 +5737,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
ReleaseSysCache(typeTuple);
- InsertPgAttributeTuple(attrdesc, &attribute, NULL);
+ InsertPgAttributeTuple(attrdesc, &attribute, (Datum) 0, NULL);
table_close(attrdesc, RowExclusiveLock);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index a2617c7..d44a5b0 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2868,6 +2868,7 @@ _copyIndexElem(const IndexElem *from)
COPY_STRING_FIELD(indexcolname);
COPY_NODE_FIELD(collation);
COPY_NODE_FIELD(opclass);
+ COPY_NODE_FIELD(opclassopts);
COPY_SCALAR_FIELD(ordering);
COPY_SCALAR_FIELD(nulls_ordering);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 4f2ebe5..6b14095 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2546,6 +2546,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b)
COMPARE_STRING_FIELD(indexcolname);
COMPARE_NODE_FIELD(collation);
COMPARE_NODE_FIELD(opclass);
+ COMPARE_NODE_FIELD(opclassopts);
COMPARE_SCALAR_FIELD(ordering);
COMPARE_SCALAR_FIELD(nulls_ordering);
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 18466ac..12d43bd 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -763,6 +763,9 @@ makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions,
n->ii_ExclusionProcs = NULL;
n->ii_ExclusionStrats = NULL;
+ /* opclass options */
+ n->ii_OpclassOptions = NULL;
+
/* speculative inserts */
n->ii_UniqueOps = NULL;
n->ii_UniqueProcs = NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e6ce8e2..37b6d6f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2850,6 +2850,7 @@ _outIndexElem(StringInfo str, const IndexElem *node)
WRITE_STRING_FIELD(indexcolname);
WRITE_NODE_FIELD(collation);
WRITE_NODE_FIELD(opclass);
+ WRITE_NODE_FIELD(opclassopts);
WRITE_ENUM_FIELD(ordering, SortByDir);
WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index cf17614..de9b6f4 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -279,6 +279,9 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->amcostestimate = amroutine->amcostestimate;
Assert(info->amcostestimate != NULL);
+ /* Fetch index opclass options */
+ info->opclassoptions = RelationGetIndexAttOptions(indexRelation, true);
+
/*
* Fetch the ordering information for the index, if any.
*/
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a954acf..c8b5d91 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -491,7 +491,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <alias> alias_clause opt_alias_clause
%type <list> func_alias_clause
%type <sortby> sortby
-%type <ielem> index_elem
+%type <ielem> index_elem index_elem_options
%type <node> table_ref
%type <jexpr> joined_table
%type <range> relation_expr
@@ -7420,43 +7420,65 @@ index_params: index_elem { $$ = list_make1($1); }
| index_params ',' index_elem { $$ = lappend($1, $3); }
;
+
+index_elem_options:
+ opt_collate opt_class opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = NIL;
+ $$->ordering = $3;
+ $$->nulls_ordering = $4;
+ }
+ | opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ | opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = NIL;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ ;
+
/*
* Index attributes can be either simple column references, or arbitrary
* expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens.
*/
-index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
{
- $$ = makeNode(IndexElem);
+ $$ = $2;
$$->name = $1;
- $$->expr = NULL;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+ | func_expr_windowless index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $2;
$$->expr = $1;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+ | '(' a_expr ')' index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $4;
$$->expr = $2;
- $$->indexcolname = NULL;
- $$->collation = $4;
- $$->opclass = $5;
- $$->ordering = $6;
- $$->nulls_ordering = $7;
}
;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 6e5768c..5872376 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1577,6 +1577,8 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
/* Add the operator class name, if non-default */
iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
+ iparam->opclassopts =
+ untransformRelOptions(get_attoptions(source_relid, keyno + 1));
iparam->ordering = SORTBY_DEFAULT;
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
@@ -2153,9 +2155,13 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
* semantics as you'd get from a normally-created constraint;
* and there's also the dump/reload problem mentioned above.
*/
+ Datum attoptions =
+ get_attoptions(RelationGetRelid(index_rel), i + 1);
+
defopclass = GetDefaultOpClass(attform->atttypid,
index_rel->rd_rel->relam);
if (indclass->values[i] != defopclass ||
+ attoptions != (Datum) 0 ||
index_rel->rd_indoption[i] != 0)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -2335,6 +2341,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
iparam->indexcolname = NULL;
iparam->collation = NIL;
iparam->opclass = NIL;
+ iparam->opclassopts = NIL;
iparam->ordering = SORTBY_DEFAULT;
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
index->indexParams = lappend(index->indexParams, iparam);
@@ -2448,6 +2455,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
iparam->indexcolname = NULL;
iparam->collation = NIL;
iparam->opclass = NIL;
+ iparam->opclassopts = NIL;
index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3e64390..faecf04 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -469,6 +469,7 @@ static void add_cast_to(StringInfo buf, Oid typid);
static char *generate_qualified_type_name(Oid typid);
static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
+static void get_reloptions(StringInfo buf, Datum reloptions);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
@@ -1371,6 +1372,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
{
int16 opt = indoption->values[keyno];
Oid indcoll = indcollation->values[keyno];
+ Datum attoptions = get_attoptions(indexrelid, keyno + 1);
+ bool has_options = attoptions != (Datum) 0;
/* Add collation, if not default for column */
if (OidIsValid(indcoll) && indcoll != keycolcollation)
@@ -1378,7 +1381,15 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
generate_collation_name((indcoll)));
/* Add the operator class name, if not default */
- get_opclass_name(indclass->values[keyno], keycoltype, &buf);
+ get_opclass_name(indclass->values[keyno],
+ has_options ? InvalidOid : keycoltype, &buf);
+
+ if (has_options)
+ {
+ appendStringInfoString(&buf, " (");
+ get_reloptions(&buf, attoptions);
+ appendStringInfoChar(&buf, ')');
+ }
/* Add options if relevant */
if (amroutine->amcanorder)
@@ -10506,6 +10517,23 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
}
/*
+ * generate_opclass_name
+ * Compute the name to display for a opclass specified by OID
+ *
+ * The result includes all necessary quoting and schema-prefixing.
+ */
+char *
+generate_opclass_name(Oid opclass)
+{
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+ get_opclass_name(opclass, InvalidOid, &buf);
+
+ return &buf.data[1]; /* get_opclass_name() prepends space */
+}
+
+/*
* processIndirection - take care of array and subfield assignment
*
* We strip any top-level FieldStore or assignment SubscriptingRef nodes that
@@ -11183,6 +11211,62 @@ string_to_text(char *str)
}
/*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+ Datum *options;
+ int noptions;
+ int i;
+
+ deconstruct_array(DatumGetArrayTypeP(reloptions),
+ TEXTOID, -1, false, 'i',
+ &options, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *option = TextDatumGetCString(options[i]);
+ char *name;
+ char *separator;
+ char *value;
+
+ /*
+ * Each array element should have the form name=value. If the "="
+ * is missing for some reason, treat it like an empty value.
+ */
+ name = option;
+ separator = strchr(option, '=');
+ if (separator)
+ {
+ *separator = '\0';
+ value = separator + 1;
+ }
+ else
+ value = "";
+
+ if (i > 0)
+ appendStringInfoString(buf, ", ");
+ appendStringInfo(buf, "%s=", quote_identifier(name));
+
+ /*
+ * In general we need to quote the value; but to avoid unnecessary
+ * clutter, do not quote if it is an identifier that would not
+ * need quoting. (We could also allow numbers, but that is a bit
+ * trickier than it looks --- for example, are leading zeroes
+ * significant? We don't want to assume very much here about what
+ * custom reloptions might mean.)
+ */
+ if (quote_identifier(value) == value)
+ appendStringInfoString(buf, value);
+ else
+ simple_quote_literal(buf, value);
+
+ pfree(option);
+ }
+}
+
+/*
* Generate a C string representing a relation's reloptions, or NULL if none.
*/
static char *
@@ -11202,56 +11286,9 @@ flatten_reloptions(Oid relid)
if (!isnull)
{
StringInfoData buf;
- Datum *options;
- int noptions;
- int i;
initStringInfo(&buf);
-
- deconstruct_array(DatumGetArrayTypeP(reloptions),
- TEXTOID, -1, false, 'i',
- &options, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *option = TextDatumGetCString(options[i]);
- char *name;
- char *separator;
- char *value;
-
- /*
- * Each array element should have the form name=value. If the "="
- * is missing for some reason, treat it like an empty value.
- */
- name = option;
- separator = strchr(option, '=');
- if (separator)
- {
- *separator = '\0';
- value = separator + 1;
- }
- else
- value = "";
-
- if (i > 0)
- appendStringInfoString(&buf, ", ");
- appendStringInfo(&buf, "%s=", quote_identifier(name));
-
- /*
- * In general we need to quote the value; but to avoid unnecessary
- * clutter, do not quote if it is an identifier that would not
- * need quoting. (We could also allow numbers, but that is a bit
- * trickier than it looks --- for example, are leading zeroes
- * significant? We don't want to assume very much here about what
- * custom reloptions might mean.)
- */
- if (quote_identifier(value) == value)
- appendStringInfoString(&buf, value);
- else
- simple_quote_literal(&buf, value);
-
- pfree(option);
- }
+ get_reloptions(&buf, reloptions);
result = buf.data;
}
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 1710129..638d5ad 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -6260,6 +6260,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
Oid clause_op, Datum query,
GinQualCounts *counts)
{
+ FmgrInfo flinfo;
Oid extractProcOid;
Oid collation;
int strategy_op;
@@ -6309,15 +6310,19 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
else
collation = DEFAULT_COLLATION_OID;
- OidFunctionCall7Coll(extractProcOid,
- collation,
- query,
- PointerGetDatum(&nentries),
- UInt16GetDatum(strategy_op),
- PointerGetDatum(&partial_matches),
- PointerGetDatum(&extra_data),
- PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode));
+ fmgr_info(extractProcOid, &flinfo);
+
+ set_fn_opclass_options(&flinfo, index->opclassoptions[indexcol]);
+
+ FunctionCall7Coll(&flinfo,
+ collation,
+ query,
+ PointerGetDatum(&nentries),
+ UInt16GetDatum(strategy_op),
+ PointerGetDatum(&partial_matches),
+ PointerGetDatum(&extra_data),
+ PointerGetDatum(&nullFlags),
+ PointerGetDatum(&searchMode));
if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
{
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27602fa..8fbf5e3 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -908,6 +908,41 @@ get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
ReleaseSysCache(tp);
}
+/*
+ * get_attoptions
+ *
+ * Given the relation id and the attribute number,
+ * return the attribute options text[] datum, if any.
+ */
+Datum
+get_attoptions(Oid relid, int16 attnum)
+{
+ HeapTuple tuple;
+ Datum attopts;
+ Datum result;
+ bool isnull;
+
+ tuple = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, relid);
+
+ attopts = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
+ &isnull);
+
+ if (isnull)
+ result = (Datum) 0;
+ else
+ result = datumCopy(attopts, false, -1); /* text[] */
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
/* ---------- COLLATION CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 585dcee..7f96935 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1438,7 +1438,7 @@ RelationInitIndexAccessInfo(Relation relation)
amsupport = relation->rd_indam->amsupport;
if (amsupport > 0)
{
- int nsupport = indnatts * amsupport;
+ int nsupport = indnatts * (amsupport + 1);
relation->rd_support = (RegProcedure *)
MemoryContextAllocZero(indexcxt, nsupport * sizeof(RegProcedure));
@@ -1502,6 +1502,10 @@ RelationInitIndexAccessInfo(Relation relation)
indoption = (int2vector *) DatumGetPointer(indoptionDatum);
memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16));
+#if 0
+ (void) RelationGetIndexAttOptions(relation, false);
+#endif
+
/*
* expressions, predicate, exclusion caches will be filled later
*/
@@ -1551,9 +1555,9 @@ IndexSupportInitialize(oidvector *indclass,
opFamily[attIndex] = opcentry->opcfamily;
opcInType[attIndex] = opcentry->opcintype;
if (maxSupportNumber > 0)
- memcpy(&indexSupport[attIndex * maxSupportNumber],
+ memcpy(&indexSupport[attIndex * (maxSupportNumber + 1)],
opcentry->supportProcs,
- maxSupportNumber * sizeof(RegProcedure));
+ (maxSupportNumber + 1) * sizeof(RegProcedure));
}
}
@@ -1618,7 +1622,7 @@ LookupOpclassInfo(Oid operatorClassOid,
if (numSupport > 0)
opcentry->supportProcs = (RegProcedure *)
MemoryContextAllocZero(CacheMemoryContext,
- numSupport * sizeof(RegProcedure));
+ (numSupport + 1) * sizeof(RegProcedure));
else
opcentry->supportProcs = NULL;
}
@@ -1705,13 +1709,12 @@ LookupOpclassInfo(Oid operatorClassOid,
{
Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(htup);
- if (amprocform->amprocnum <= 0 ||
+ if (amprocform->amprocnum < 0 ||
(StrategyNumber) amprocform->amprocnum > numSupport)
elog(ERROR, "invalid amproc number %d for opclass %u",
amprocform->amprocnum, operatorClassOid);
- opcentry->supportProcs[amprocform->amprocnum - 1] =
- amprocform->amproc;
+ opcentry->supportProcs[amprocform->amprocnum] = amprocform->amproc;
}
systable_endscan(scan);
@@ -3998,6 +4001,10 @@ load_critical_index(Oid indexoid, Oid heapoid)
ird->rd_refcnt = 1;
UnlockRelationOid(indexoid, AccessShareLock);
UnlockRelationOid(heapoid, AccessShareLock);
+
+#if 0
+ (void) RelationGetIndexAttOptions(ird, false);
+#endif
}
/*
@@ -5153,6 +5160,99 @@ GetRelationPublicationActions(Relation relation)
}
/*
+ * RelationGetIndexRawAttOptions -- get AM/opclass-specific options for the index
+ */
+Datum *
+RelationGetIndexRawAttOptions(Relation indexrel)
+{
+ Oid indexrelid = RelationGetRelid(indexrel);
+ int16 natts = RelationGetNumberOfAttributes(indexrel);
+ Datum *options = NULL;
+ int16 attnum;
+
+ for (attnum = 1; attnum <= natts; attnum++)
+ {
+ if (!OidIsValid(index_getprocid(indexrel, attnum, OPCLASS_OPTIONS_PROC)))
+ continue;
+
+ if (!options)
+ options = palloc0(sizeof(Datum) * natts);
+
+ options[attnum - 1] = get_attoptions(indexrelid, attnum);
+ }
+
+ return options;
+}
+
+static bytea **
+CopyIndexAttOptions(bytea **srcopts, int natts)
+{
+ bytea **opts = palloc(sizeof(*opts) * natts);
+
+ for (int i = 0; i < natts; i++)
+ {
+ bytea *opt = srcopts[i];
+
+ opts[i] = !opt ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+ }
+
+ return opts;
+}
+
+/*
+ * RelationGetIndexAttOptions
+ * get AM/opclass-specific options for an index parsed into a binary form
+ */
+bytea **
+RelationGetIndexAttOptions(Relation relation, bool copy)
+{
+ MemoryContext oldcxt;
+ bytea **opts = relation->rd_opcoptions;
+ Oid relid = RelationGetRelid(relation);
+ int natts = RelationGetNumberOfAttributes(relation); /* XXX IndexRelationGetNumberOfKeyAttributes */
+ int i;
+
+ /* Try to copy cached options. */
+ if (opts)
+ return copy ? CopyIndexAttOptions(opts, natts) : opts;
+
+ /* Get and parse opclass options. */
+ opts = palloc0(sizeof(*opts) * natts);
+
+ for (i = 0; i < natts; i++)
+ {
+ if (criticalRelcachesBuilt && relid != AttributeRelidNumIndexId)
+ {
+ Datum attoptions = get_attoptions(relid, i + 1);
+
+ opts[i] = index_opclass_options(relation, i + 1, attoptions, false);
+
+ if (attoptions != (Datum) 0)
+ pfree(DatumGetPointer(attoptions));
+ }
+ }
+
+ /* Copy parsed options to the cache. */
+ oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
+ relation->rd_opcoptions = CopyIndexAttOptions(opts, natts);
+ MemoryContextSwitchTo(oldcxt);
+
+ if (copy)
+ return opts;
+
+ for (i = 0; i < natts; i++)
+ {
+ if (opts[i])
+ pfree(opts[i]);
+ }
+
+ pfree(opts);
+
+ return relation->rd_opcoptions;
+}
+
+/*
* Routines to support ereport() reports of relation-related errors
*
* These could have been put into elog.c, but it seems like a module layering
@@ -5513,8 +5613,27 @@ load_relcache_init_file(bool shared)
rel->rd_indoption = indoption;
+#if 0
+ /* finally, read the vector of opcoptions values */
+ rel->rd_opcoptions = (bytea **)
+ MemoryContextAllocZero(indexcxt, sizeof(*rel->rd_opcoptions) * relform->relnatts);
+
+ for (i = 0; i < relform->relnatts; i++)
+ {
+ if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
+ goto read_failed;
+
+ if (len > 0)
+ {
+ rel->rd_opcoptions[i] = (bytea *) MemoryContextAlloc(indexcxt, len);
+ if (fread(rel->rd_opcoptions[i], 1, len, fp) != len)
+ goto read_failed;
+ }
+ }
+#endif
+
/* set up zeroed fmgr-info vector */
- nsupport = relform->relnatts * rel->rd_indam->amsupport;
+ nsupport = relform->relnatts * (rel->rd_indam->amsupport + 1);
rel->rd_supportinfo = (FmgrInfo *)
MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
}
@@ -5541,6 +5660,7 @@ load_relcache_init_file(bool shared)
Assert(rel->rd_supportinfo == NULL);
Assert(rel->rd_indoption == NULL);
Assert(rel->rd_indcollation == NULL);
+ Assert(rel->rd_opcoptions == NULL);
}
/*
@@ -5568,6 +5688,7 @@ load_relcache_init_file(bool shared)
rel->rd_exclprocs = NULL;
rel->rd_exclstrats = NULL;
rel->rd_fdwroutine = NULL;
+ rel->rd_opcoptions = NULL;
/*
* Reset transient-state fields in the relcache entry
@@ -5814,7 +5935,7 @@ write_relcache_init_file(bool shared)
/* next, write the vector of support procedure OIDs */
write_item(rel->rd_support,
- relform->relnatts * (rel->rd_indam->amsupport * sizeof(RegProcedure)),
+ relform->relnatts * ((rel->rd_indam->amsupport + 1) * sizeof(RegProcedure)),
fp);
/* next, write the vector of collation OIDs */
@@ -5826,6 +5947,18 @@ write_relcache_init_file(bool shared)
write_item(rel->rd_indoption,
relform->relnatts * sizeof(int16),
fp);
+
+#if 0
+ Assert(rel->rd_opcoptions);
+
+ /* finally, write the vector of opcoptions values */
+ for (i = 0; i < relform->relnatts; i++)
+ {
+ bytea *opt = rel->rd_opcoptions[i];
+
+ write_item(opt, opt ? VARSIZE(opt) : 0, fp);
+ }
+#endif
}
}
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 0484adb..e11146a 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -18,9 +18,11 @@
#include "access/detoast.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
#include "executor/functions.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
+#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "pgstat.h"
#include "utils/acl.h"
@@ -1967,6 +1969,32 @@ get_fn_expr_variadic(FmgrInfo *flinfo)
return false;
}
+void
+set_fn_opclass_options(FmgrInfo *flinfo, bytea *options)
+{
+ flinfo->fn_expr = (Node *) makeConst(BYTEAOID, -1, InvalidOid, -1,
+ PointerGetDatum(options),
+ options == NULL, false);
+}
+
+bytea *
+get_fn_opclass_options(FmgrInfo *flinfo)
+{
+ if (flinfo && flinfo->fn_expr && IsA(flinfo->fn_expr, Const))
+ {
+ Const *expr = (Const *) flinfo->fn_expr;
+
+ if (expr->consttype == BYTEAOID)
+ return expr->constisnull ? NULL : DatumGetByteaP(expr->constvalue);
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("opclass options info is absent in function call context")));
+
+ return NULL;
+}
+
/*-------------------------------------------------------------------------
* Support routines for procedural language implementations
*-------------------------------------------------------------------------
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 6c56717..778d17c 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -182,6 +182,9 @@ extern void index_store_float8_orderby_distances(IndexScanDesc scan,
double *distanceValues,
bool *distanceNulls,
bool recheckOrderBy);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+ Datum attoptions, bool validate);
+
/*
* index access method support routines (in genam.c)
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 6d392e4..70238f9 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -37,6 +37,7 @@ typedef enum relopt_type
/* kinds supported by reloptions */
typedef enum relopt_kind
{
+ RELOPT_KIND_LOCAL = 0,
RELOPT_KIND_HEAP = (1 << 0),
RELOPT_KIND_TOAST = (1 << 1),
RELOPT_KIND_BTREE = (1 << 2),
@@ -109,6 +110,9 @@ typedef struct relopt_real
/* validation routines for strings */
typedef void (*validate_string_relopt) (const char *value);
+typedef Size (*fill_string_relopt) (const char *value, void *ptr);
+
+typedef void (*relopts_validator)(void *parsed_options, relopt_value *vals, int nvals);
typedef struct relopt_string
{
@@ -116,6 +120,7 @@ typedef struct relopt_string
int default_len;
bool default_isnull;
validate_string_relopt validate_cb;
+ fill_string_relopt fill_cb;
char *default_val;
} relopt_string;
@@ -127,6 +132,13 @@ typedef struct
int offset; /* offset of field in result struct */
} relopt_parse_elt;
+typedef struct local_relopts
+{
+ List *options;
+ List *validators;
+ void *base;
+ Size base_size;
+} local_relopts;
/*
* These macros exist for the convenience of amoptions writers (but consider
@@ -255,6 +267,26 @@ extern void add_real_reloption(bits32 kinds, const char *name, const char *desc,
extern void add_string_reloption(bits32 kinds, const char *name, const char *desc,
const char *default_val, validate_string_relopt validator);
+extern void init_local_reloptions(local_relopts *opts, void *base, Size base_size);
+extern void extend_local_reloptions(local_relopts *opts, void *base, Size base_size);
+extern void register_reloptions_validator(local_relopts *opts,
+ relopts_validator validator);
+extern void add_local_bool_reloption(local_relopts *opts, const char *name,
+ const char *desc, bool default_val,
+ bool *pval);
+extern void add_local_int_reloption(local_relopts *opts, const char *name,
+ const char *desc, int default_val,
+ int min_val, int max_val, int *pval);
+extern void add_local_real_reloption(local_relopts *opts, const char *name,
+ const char *desc, double default_val,
+ double min_val, double max_val,
+ double *pval);
+extern void add_local_string_reloption(local_relopts *opts, const char *name,
+ const char *desc,
+ const char *default_val,
+ validate_string_relopt validator,
+ fill_string_relopt filler, int *poffset);
+
extern Datum transformRelOptions(Datum oldOptions, List *defList,
const char *namspace, char *validnsps[],
bool acceptOidsOff, bool isReset);
@@ -263,12 +295,16 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
relopt_kind kind, int *numrelopts);
+extern relopt_value *parseLocalRelOptions(local_relopts *relopts, Datum options,
+ bool validate);
extern void *allocateReloptStruct(Size base, relopt_value *options,
int numoptions);
extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern void *parseAndFillLocalRelOptions(local_relopts *relopts, Datum options,
+ bool validate);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index eec71c2..866e94d 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -94,6 +94,7 @@ extern List *heap_truncate_find_FKs(List *relationIds);
extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
Form_pg_attribute new_attribute,
+ Datum attoptions,
CatalogIndexState indstate);
extern void InsertPgClassTuple(Relation pg_class_desc,
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index 62ca564..75cb340 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -67,4 +67,6 @@ CATALOG(pg_amproc,2603,AccessMethodProcedureRelationId)
*/
typedef FormData_pg_amproc *Form_pg_amproc;
+#define OPCLASS_OPTIONS_PROC 0 /* amprocnum for opclass options */
+
#endif /* PG_AMPROC_H */
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 29ae467..568a9c6 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -331,6 +331,8 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
#define PG_GETARG_BPCHAR_P(n) DatumGetBpCharP(PG_GETARG_DATUM(n))
#define PG_GETARG_VARCHAR_P(n) DatumGetVarCharP(PG_GETARG_DATUM(n))
+#define PG_GET_OPCLASS_OPTIONS() get_fn_opclass_options(fcinfo->flinfo)
+
/* To return a NULL do this: */
#define PG_RETURN_NULL() \
do { fcinfo->isnull = true; return (Datum) 0; } while (0)
@@ -699,6 +701,8 @@ extern Oid get_call_expr_argtype(fmNodePtr expr, int argnum);
extern bool get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum);
extern bool get_call_expr_arg_stable(fmNodePtr expr, int argnum);
extern bool get_fn_expr_variadic(FmgrInfo *flinfo);
+extern bytea *get_fn_opclass_options(FmgrInfo *flinfo);
+extern void set_fn_opclass_options(FmgrInfo *flinfo, bytea *options);
extern bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid);
/*
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index b593d22..967c818 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -139,6 +139,7 @@ typedef struct ExprState
* UniqueProcs
* UniqueStrats
* Unique is it a unique index?
+ * OpclassOptions opclass-specific options, or NULL if none
* ReadyForInserts is it valid for inserts?
* Concurrent are we doing a concurrent index build?
* BrokenHotChain did we detect any broken HOT chains?
@@ -167,6 +168,7 @@ typedef struct IndexInfo
Oid *ii_UniqueOps; /* array with one entry per column */
Oid *ii_UniqueProcs; /* array with one entry per column */
uint16 *ii_UniqueStrats; /* array with one entry per column */
+ Datum *ii_OpclassOptions; /* array with one entry per column */
bool ii_Unique;
bool ii_ReadyForInserts;
bool ii_Concurrent;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 94ded3c..aa266b2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -701,6 +701,7 @@ typedef struct IndexElem
char *indexcolname; /* name for index column; NULL = default */
List *collation; /* name of collation; NIL = default */
List *opclass; /* name of desired opclass; NIL = default */
+ List *opclassopts; /* opclass-specific options, or NIL */
SortByDir ordering; /* ASC/DESC/default */
SortByNulls nulls_ordering; /* FIRST/LAST/default */
} IndexElem;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 23a06d7..a07c44d 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -806,6 +806,7 @@ struct IndexOptInfo
Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */
bool *reverse_sort; /* is sort order descending? */
bool *nulls_first; /* do NULLs come first in the sort order? */
+ bytea **opclassoptions; /* opclass-specific options for columns */
bool *canreturn; /* which index cols can be returned in an
* index-only scan? */
Oid relam; /* OID of the access method (in pg_am) */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index c8df5bf..2fa9965 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -90,6 +90,7 @@ extern char get_attgenerated(Oid relid, AttrNumber attnum);
extern Oid get_atttype(Oid relid, AttrNumber attnum);
extern void get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
Oid *typid, int32 *typmod, Oid *collid);
+extern Datum get_attoptions(Oid relid, int16 attnum);
extern char *get_collation_name(Oid colloid);
extern bool get_collation_isdeterministic(Oid colloid);
extern char *get_constraint_name(Oid conoid);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 91b3b1b..552d9eb 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -166,6 +166,7 @@ typedef struct RelationData
Oid *rd_exclprocs; /* OIDs of exclusion ops' procs, if any */
uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */
Oid *rd_indcollation; /* OIDs of index collations */
+ bytea **rd_opcoptions; /* parsed opclass-specific options */
/*
* rd_amcache is available for index and table AMs to cache private data
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 2f2ace3..adc2b26 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -14,6 +14,7 @@
#ifndef RELCACHE_H
#define RELCACHE_H
+#include "postgres.h"
#include "access/tupdesc.h"
#include "nodes/bitmapset.h"
@@ -49,6 +50,8 @@ extern Oid RelationGetPrimaryKeyIndex(Relation relation);
extern Oid RelationGetReplicaIndex(Relation relation);
extern List *RelationGetIndexExpressions(Relation relation);
extern List *RelationGetIndexPredicate(Relation relation);
+extern Datum *RelationGetIndexRawAttOptions(Relation relation);
+extern bytea **RelationGetIndexAttOptions(Relation relation, bool copy);
typedef enum IndexAttrBitmapKind
{
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index d34cad2..e295052 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -34,6 +34,7 @@ extern List *set_deparse_context_planstate(List *dpcontext,
extern List *select_rtable_names_for_explain(List *rtable,
Bitmapset *rels_used);
extern char *generate_collation_name(Oid collid);
+extern char *generate_opclass_name(Oid opclass);
extern char *get_range_partbound_string(List *bound_datums);
#endif /* RULEUTILS_H */
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index 8663f0c..40b3267 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -353,10 +353,11 @@ ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- ope
ERROR: invalid operator number 0, must be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ERROR: invalid function number 0, must be between 1 and 3
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ERROR: invalid function number 6, must be between 1 and 3
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function
+ERROR: invalid opclass options parsing function
+HINT: opclass options parsing function must have signature '(internal) RETURNS void'
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 4 btint42cmp(int4, int2); -- function number should be between 0 and 3
+ERROR: invalid function number 4, must be between 0 and 3
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY
ERROR: STORAGE cannot be specified in ALTER OPERATOR FAMILY
DROP OPERATOR FAMILY alt_opf4 USING btree;
@@ -496,6 +497,19 @@ ALTER OPERATOR FAMILY alt_opf18 USING btree ADD
ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
ERROR: function 2(integer,integer) does not exist in operator family "alt_opf18"
DROP OPERATOR FAMILY alt_opf18 USING btree;
+-- Should fail. Invalid opclass options function (#0) specifications.
+CREATE OPERATOR FAMILY alt_opf19 USING btree;
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 test_opclass_options_func(internal, text[], bool);
+ERROR: function test_opclass_options_func(internal, text[], boolean) does not exist
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4) btint42cmp(int4, int2);
+ERROR: invalid opclass options parsing function
+HINT: opclass options parsing function must have signature '(internal) RETURNS void'
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4, int2) btint42cmp(int4, int2);
+ERROR: left and right associated data types for opclass options parsing functions must match
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4) test_opclass_options_func(internal); -- Ok
+ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 0 (int4, int4);
+ERROR: invalid function number 0, must be between 1 and 3
+DROP OPERATOR FAMILY alt_opf19 USING btree;
--
-- Statistics
--
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index acab8e0..b4023de 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -262,3 +262,8 @@ VACUUM delete_test_table;
-- The vacuum above should've turned the leaf page into a fast root. We just
-- need to insert some rows to cause the fast root page to split.
INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR: operator class int4_ops has no options
+create index on btree_tall_tbl (id default(foo=1));
+ERROR: operator class int4_ops has no options
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 33c058f..1f53a9c 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2054,7 +2054,7 @@ WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND
SELECT p1.amprocfamily, p1.amprocnum
FROM pg_amproc as p1
WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
- OR p1.amprocnum < 1 OR p1.amproc = 0;
+ OR p1.amprocnum < 0 OR p1.amproc = 0;
amprocfamily | amprocnum
--------------+-----------
(0 rows)
diff --git a/src/test/regress/input/create_function_1.source b/src/test/regress/input/create_function_1.source
index 223454a..412e339 100644
--- a/src/test/regress/input/create_function_1.source
+++ b/src/test/regress/input/create_function_1.source
@@ -73,6 +73,11 @@ CREATE FUNCTION test_support_func(internal)
AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
LANGUAGE C STRICT;
+CREATE FUNCTION test_opclass_options_func(internal)
+ RETURNS void
+ AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
+ LANGUAGE C;
+
-- Things that shouldn't work:
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
diff --git a/src/test/regress/output/create_function_1.source b/src/test/regress/output/create_function_1.source
index 5f43e8d..4d78fa1 100644
--- a/src/test/regress/output/create_function_1.source
+++ b/src/test/regress/output/create_function_1.source
@@ -64,6 +64,10 @@ CREATE FUNCTION test_support_func(internal)
RETURNS internal
AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
LANGUAGE C STRICT;
+CREATE FUNCTION test_opclass_options_func(internal)
+ RETURNS void
+ AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
+ LANGUAGE C;
-- Things that shouldn't work:
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
AS 'SELECT ''not an integer'';';
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index 826556e..3ade2d6 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -940,3 +940,10 @@ test_support_func(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(ret);
}
+
+PG_FUNCTION_INFO_V1(test_opclass_options_func);
+Datum
+test_opclass_options_func(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_NULL();
+}
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index ce9dbb1..31ad01c 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -298,8 +298,8 @@ ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 4 btint42cmp(int4, int2); -- function number should be between 0 and 3
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY
DROP OPERATOR FAMILY alt_opf4 USING btree;
@@ -433,6 +433,15 @@ ALTER OPERATOR FAMILY alt_opf18 USING btree ADD
ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
DROP OPERATOR FAMILY alt_opf18 USING btree;
+-- Should fail. Invalid opclass options function (#0) specifications.
+CREATE OPERATOR FAMILY alt_opf19 USING btree;
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 test_opclass_options_func(internal, text[], bool);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4) btint42cmp(int4, int2);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4, int2) btint42cmp(int4, int2);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4) test_opclass_options_func(internal); -- Ok
+ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 0 (int4, int4);
+DROP OPERATOR FAMILY alt_opf19 USING btree;
+
--
-- Statistics
--
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 48eaf4f..7290c86 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -140,3 +140,7 @@ VACUUM delete_test_table;
-- The vacuum above should've turned the leaf page into a fast root. We just
-- need to insert some rows to cause the fast root page to split.
INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;
+
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+create index on btree_tall_tbl (id default(foo=1));
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 1227ef7..cee6a40 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1306,7 +1306,7 @@ WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND
SELECT p1.amprocfamily, p1.amprocnum
FROM pg_amproc as p1
WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
- OR p1.amprocnum < 1 OR p1.amproc = 0;
+ OR p1.amprocnum < 0 OR p1.amproc = 0;
-- Support routines that are primary members of opfamilies must be immutable
-- (else it suggests that the index ordering isn't fixed). But cross-type
--
2.7.4
[text/x-patch] 0002-Introduce-amattoptions-20190910.patch (7.1K, ../dcb740af-2a93-8785-eb26-a4273029f2fb@postgrespro.ru/4-0002-Introduce-amattoptions-20190910.patch)
download | inline diff:
From c9acb5bf6033c1d18ff35c58000aa0fd62bdbf73 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 2/5] Introduce amattoptions
---
contrib/bloom/blutils.c | 1 +
src/backend/access/brin/brin.c | 1 +
src/backend/access/gin/ginutil.c | 1 +
src/backend/access/gist/gist.c | 1 +
src/backend/access/hash/hash.c | 1 +
src/backend/access/index/indexam.c | 16 +++++++++++-----
src/backend/access/nbtree/nbtree.c | 1 +
src/backend/access/spgist/spgutils.c | 1 +
src/backend/utils/cache/relcache.c | 3 ++-
src/include/access/amapi.h | 7 +++++++
10 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index cc16709..b62ba7f 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -131,6 +131,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = blcostestimate;
amroutine->amoptions = bloptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = blvalidate;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index ae7b729..e86f1f9 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -111,6 +111,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = brincostestimate;
amroutine->amoptions = brinoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = brinvalidate;
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index cf9699a..1154239 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -63,6 +63,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
amroutine->amoptions = ginoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = ginvalidate;
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 0cc8791..a1f96b1 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -85,6 +85,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = gistcanreturn;
amroutine->amcostestimate = gistcostestimate;
amroutine->amoptions = gistoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = gistproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = gistvalidate;
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 5cc30da..84fb4d6 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -84,6 +84,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = hashcostestimate;
amroutine->amoptions = hashoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = hashvalidate;
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index bbed726..58b0bda 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -940,10 +940,10 @@ index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
bool validate)
{
Oid procid = index_getprocid(indrel, attnum, 0);
- FmgrInfo *procinfo;
local_relopts relopts;
+ amattoptions_function amattoptions = indrel->rd_indam->amattoptions;
- if (!OidIsValid(procid))
+ if (!OidIsValid(procid) && !amattoptions)
{
Oid opclass;
Datum indclassDatum;
@@ -969,11 +969,17 @@ index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
generate_opclass_name(opclass))));
}
- init_local_reloptions(&relopts, NULL, 0);
+ if (amattoptions)
+ amattoptions(&relopts, attnum);
+ else
+ init_local_reloptions(&relopts, NULL, 0);
- procinfo = index_getprocinfo(indrel, attnum, 0);
+ if (OidIsValid(procid))
+ {
+ FmgrInfo *procinfo = index_getprocinfo(indrel, attnum, 0);
- (void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
+ (void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
+ }
return parseAndFillLocalRelOptions(&relopts, attoptions, validate);
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 4cfd528..0741107 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -133,6 +133,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
amroutine->amoptions = btoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = btproperty;
amroutine->ambuildphasename = btbuildphasename;
amroutine->amvalidate = btvalidate;
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 45472db..0bf5734 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -66,6 +66,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = spgcanreturn;
amroutine->amcostestimate = spgcostestimate;
amroutine->amoptions = spgoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = spgproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = spgvalidate;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 7f96935..d36b31b 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5172,7 +5172,8 @@ RelationGetIndexRawAttOptions(Relation indexrel)
for (attnum = 1; attnum <= natts; attnum++)
{
- if (!OidIsValid(index_getprocid(indexrel, attnum, OPCLASS_OPTIONS_PROC)))
+ if (!indexrel->rd_indam->amattoptions &&
+ !OidIsValid(index_getprocid(indexrel, attnum, OPCLASS_OPTIONS_PROC)))
continue;
if (!options)
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 6e3db06..4e79e81 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -25,6 +25,8 @@ struct IndexPath;
/* Likewise, this file shouldn't depend on execnodes.h. */
struct IndexInfo;
+struct local_relopts;
+
/*
* Properties for amproperty API. This list covers properties known to the
@@ -103,6 +105,10 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
typedef bytea *(*amoptions_function) (Datum reloptions,
bool validate);
+/* initialize per-attribute reloptions */
+typedef void (*amattoptions_function) (struct local_relopts *options,
+ int attno);
+
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -215,6 +221,7 @@ typedef struct IndexAmRoutine
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amoptions_function amoptions;
+ amattoptions_function amattoptions; /* can be NULL */
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
--
2.7.4
[text/x-patch] 0003-Use-amattoptions-in-contrib-bloom-20190910.patch (8.3K, ../dcb740af-2a93-8785-eb26-a4273029f2fb@postgrespro.ru/5-0003-Use-amattoptions-in-contrib-bloom-20190910.patch)
download | inline diff:
From 735e199d6736569eb648e33256b3c5268c2a36eb Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 3/5] Use amattoptions in contrib/bloom
---
contrib/bloom/bloom.h | 10 +++++++++
contrib/bloom/blutils.c | 44 +++++++++++++++++++++-------------------
contrib/bloom/expected/bloom.out | 23 +++++++++++++--------
contrib/bloom/sql/bloom.sql | 9 ++++----
4 files changed, 53 insertions(+), 33 deletions(-)
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 010148e..681aaaa 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -16,6 +16,7 @@
#include "access/amapi.h"
#include "access/generic_xlog.h"
#include "access/itup.h"
+#include "access/reloptions.h"
#include "access/xlog.h"
#include "nodes/pathnodes.h"
#include "fmgr.h"
@@ -105,6 +106,13 @@ typedef struct BloomOptions
* index key */
} BloomOptions;
+/* Bloom attribute options */
+typedef struct BloomAttOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int bitSize; /* # of bits generated for this index key */
+} BloomAttOptions;
+
/*
* FreeBlockNumberArray - array of block numbers sized so that metadata fill
* all space in metapage.
@@ -206,6 +214,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *bloptions(Datum reloptions, bool validate);
+extern void blattoptions(local_relopts *relopts, int attno);
+
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index b62ba7f..b286081 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -39,7 +39,7 @@ PG_FUNCTION_INFO_V1(blhandler);
static relopt_kind bl_relopt_kind;
/* parse table for fillRelOptions */
-static relopt_parse_elt bl_relopt_tab[INDEX_MAX_KEYS + 1];
+static relopt_parse_elt bl_relopt_tab[1];
static int32 myRand(void);
static void mySrand(uint32 seed);
@@ -52,9 +52,6 @@ static void mySrand(uint32 seed);
void
_PG_init(void)
{
- int i;
- char buf[16];
-
bl_relopt_kind = add_reloption_kind();
/* Option for length of signature */
@@ -64,19 +61,6 @@ _PG_init(void)
bl_relopt_tab[0].optname = "length";
bl_relopt_tab[0].opttype = RELOPT_TYPE_INT;
bl_relopt_tab[0].offset = offsetof(BloomOptions, bloomLength);
-
- /* Number of bits for each possible index column: col1, col2, ... */
- for (i = 0; i < INDEX_MAX_KEYS; i++)
- {
- snprintf(buf, sizeof(buf), "col%d", i + 1);
- add_int_reloption(bl_relopt_kind, buf,
- "Number of bits generated for each index column",
- DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS);
- bl_relopt_tab[i + 1].optname = MemoryContextStrdup(TopMemoryContext,
- buf);
- bl_relopt_tab[i + 1].opttype = RELOPT_TYPE_INT;
- bl_relopt_tab[i + 1].offset = offsetof(BloomOptions, bitSize[0]) + sizeof(int) * i;
- }
}
/*
@@ -86,13 +70,10 @@ static BloomOptions *
makeDefaultBloomOptions(void)
{
BloomOptions *opts;
- int i;
opts = (BloomOptions *) palloc0(sizeof(BloomOptions));
/* Convert DEFAULT_BLOOM_LENGTH from # of bits to # of words */
opts->bloomLength = (DEFAULT_BLOOM_LENGTH + SIGNWORDBITS - 1) / SIGNWORDBITS;
- for (i = 0; i < INDEX_MAX_KEYS; i++)
- opts->bitSize[i] = DEFAULT_BLOOM_BITS;
SET_VARSIZE(opts, sizeof(BloomOptions));
return opts;
}
@@ -131,7 +112,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = blcostestimate;
amroutine->amoptions = bloptions;
- amroutine->amattoptions = NULL;
+ amroutine->amattoptions = blattoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = blvalidate;
@@ -418,6 +399,7 @@ void
BloomFillMetapage(Relation index, Page metaPage)
{
BloomOptions *opts;
+ BloomAttOptions **attopts;
BloomMetaPageData *metadata;
/*
@@ -428,6 +410,11 @@ BloomFillMetapage(Relation index, Page metaPage)
if (!opts)
opts = makeDefaultBloomOptions();
+ attopts = (BloomAttOptions **) RelationGetIndexAttOptions(index, false);
+
+ for (int i = 0; i < RelationGetNumberOfAttributes(index); i++)
+ opts->bitSize[i] = attopts[i]->bitSize;
+
/*
* Initialize contents of meta page, including a copy of the options,
* which are now frozen for the life of the index.
@@ -491,3 +478,18 @@ bloptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+
+/*
+ * Parse per-attribute reloptions for bloom index, producing a BloomOptions struct.
+ */
+void
+blattoptions(local_relopts *relopts, int attno)
+{
+ BloomAttOptions *attopts = NULL;
+
+ init_local_reloptions(relopts, attopts, sizeof(*attopts));
+ add_local_int_reloption(relopts, "bits",
+ "Number of bits generated for index column",
+ DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS,
+ &attopts->bitSize);
+}
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
index 5ab9e34..3ade0b3 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -4,7 +4,7 @@ CREATE TABLE tst (
t text
);
INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits = 3), t);
SET enable_seqscan=on;
SET enable_bitmapscan=off;
SET enable_indexscan=off;
@@ -144,7 +144,7 @@ CREATE UNLOGGED TABLE tstu (
t text
);
INSERT INTO tstu SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidxu ON tstu USING bloom (i, t) WITH (col2 = 4);
+CREATE INDEX bloomidxu ON tstu USING bloom (i, t DEFAULT (bits = 4));
SET enable_seqscan=off;
SET enable_bitmapscan=on;
SET enable_indexscan=on;
@@ -214,16 +214,23 @@ ORDER BY 1;
-- relation options
--
DROP INDEX bloomidx;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1=4);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits=4), t) WITH (length=7);
SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
- reloptions
--------------------
- {length=7,col1=4}
+ reloptions
+------------
+ {length=7}
(1 row)
+SELECT attnum, attoptions FROM pg_attribute WHERE attrelid = 'bloomidx'::regclass ORDER BY 1;
+ attnum | attoptions
+--------+------------
+ 1 | {bits=4}
+ 2 |
+(2 rows)
+
-- check for min and max values
\set VERBOSITY terse
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
ERROR: value 0 out of bounds for option "length"
-CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
-ERROR: value 0 out of bounds for option "col1"
+CREATE INDEX bloomidx2 ON tst USING bloom (i DEFAULT (bits=0), t);
+ERROR: value 0 out of bounds for option "bits"
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 32755f2..362647a 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -6,7 +6,7 @@ CREATE TABLE tst (
);
INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits = 3), t);
SET enable_seqscan=on;
SET enable_bitmapscan=off;
@@ -58,7 +58,7 @@ CREATE UNLOGGED TABLE tstu (
);
INSERT INTO tstu SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidxu ON tstu USING bloom (i, t) WITH (col2 = 4);
+CREATE INDEX bloomidxu ON tstu USING bloom (i, t DEFAULT (bits = 4));
SET enable_seqscan=off;
SET enable_bitmapscan=on;
@@ -86,9 +86,10 @@ ORDER BY 1;
-- relation options
--
DROP INDEX bloomidx;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1=4);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits=4), t) WITH (length=7);
SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+SELECT attnum, attoptions FROM pg_attribute WHERE attrelid = 'bloomidx'::regclass ORDER BY 1;
-- check for min and max values
\set VERBOSITY terse
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
-CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i DEFAULT (bits=0), t);
--
2.7.4
[text/x-patch] 0004-Use-opclass-parameters-in-GiST-tsvector_ops-20190910.patch (29.1K, ../dcb740af-2a93-8785-eb26-a4273029f2fb@postgrespro.ru/6-0004-Use-opclass-parameters-in-GiST-tsvector_ops-20190910.patch)
download | inline diff:
From d0b53a9ab7dbd04206be08c03ae19ded15e7e921 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 4/5] Use opclass parameters in GiST tsvector_ops
---
doc/src/sgml/textsearch.sgml | 9 +-
src/backend/utils/adt/tsgistidx.c | 266 ++++++++++++++++++----------------
src/include/catalog/pg_amproc.dat | 3 +
src/include/catalog/pg_proc.dat | 3 +
src/test/regress/expected/tsearch.out | 176 ++++++++++++++++++++++
src/test/regress/sql/tsearch.sql | 45 ++++++
6 files changed, 379 insertions(+), 123 deletions(-)
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index 3f00ae3..2e881f7 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3637,7 +3637,7 @@ SELECT plainto_tsquery('supernovae stars');
<tertiary>text search</tertiary>
</indexterm>
- <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+ <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
</term>
<listitem>
@@ -3645,6 +3645,8 @@ SELECT plainto_tsquery('supernovae stars');
Creates a GiST (Generalized Search Tree)-based index.
The <replaceable>column</replaceable> can be of <type>tsvector</type> or
<type>tsquery</type> type.
+ Optional integer parameter <literal>siglen</literal> determines
+ signature length in bytes (see below for details).
</para>
</listitem>
</varlistentry>
@@ -3668,7 +3670,10 @@ SELECT plainto_tsquery('supernovae stars');
to check the actual table row to eliminate such false matches.
(<productname>PostgreSQL</productname> does this automatically when needed.)
GiST indexes are lossy because each document is represented in the
- index by a fixed-length signature. The signature is generated by hashing
+ index by a fixed-length signature. Signature length in bytes is determined
+ by the value of the optional integer parameter <literal>siglen</literal>.
+ Default signature length (when <literal>siglen</literal> is not specied) is
+ 124 bytes, maximal length is 484 bytes. The signature is generated by hashing
each word into a single bit in an n-bit string, with all these bits OR-ed
together to produce an n-bit document signature. When two words hash to
the same bit position there will be a false match. If all words in
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 6ff71a4..85582cd 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -16,23 +16,29 @@
#include "access/gist.h"
#include "access/heaptoast.h"
+#include "access/reloptions.h"
#include "port/pg_bitutils.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/pg_crc.h"
-#define SIGLENINT 31 /* >121 => key will toast, so it will not work
- * !!! */
+#define SIGLEN_DEFAULT (31 * 4)
+#define SIGLEN_MAX (121 * 4) /* key will toast, so it will not work !!! */
-#define SIGLEN ( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
+
+/* tsvector_ops opclass options */
+typedef struct GistTsVectorOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length */
+} GistTsVectorOptions;
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < siglen; i++)
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
#define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -40,8 +46,8 @@ typedef char *BITVECP;
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITS_PER_BYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
#define GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key))
@@ -65,13 +71,14 @@ typedef struct
#define ISALLTRUE(x) ( ((SignTSVector*)(x))->flag & ALLISTRUE )
#define GTHDRSIZE ( VARHDRSZ + sizeof(int32) )
-#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
#define GETSIGN(x) ( (BITVECP)( (char*)(x)+GTHDRSIZE ) )
+#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE )
#define GETARR(x) ( (int32*)( (char*)(x)+GTHDRSIZE ) )
#define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) )
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
Datum
gtsvectorin(PG_FUNCTION_ARGS)
@@ -102,9 +109,10 @@ gtsvectorout(PG_FUNCTION_ARGS)
sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key));
else
{
- int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key));
+ int siglen = GETSIGLEN(key);
+ int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen);
- sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue);
+ sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue);
}
PG_FREE_IF_COPY(key, 0);
@@ -148,36 +156,49 @@ uniqueint(int32 *a, int32 l)
}
static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
{
int32 k,
len = ARRNELEM(a);
int32 *ptr = GETARR(a);
- MemSet((void *) sign, 0, sizeof(BITVEC));
+ MemSet((void *) sign, 0, siglen);
for (k = 0; k < len; k++)
- HASH(sign, ptr[k]);
+ HASH(sign, ptr[k], siglen);
}
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+ int size = CALCGTSIZE(flag, len);
+ SignTSVector *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+ memcpy(GETSIGN(res), sign, len);
+
+ return res;
+}
+
+
Datum
gtsvector_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ int siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
GISTENTRY *retval = entry;
if (entry->leafkey)
{ /* tsvector */
- SignTSVector *res;
TSVector val = DatumGetTSVector(entry->key);
+ SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
int32 len;
int32 *arr;
WordEntry *ptr = ARRPTR(val);
char *words = STRPTR(val);
- len = CALCGTSIZE(ARRKEY, val->size);
- res = (SignTSVector *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = ARRKEY;
arr = GETARR(res);
len = val->size;
while (len--)
@@ -208,13 +229,9 @@ gtsvector_compress(PG_FUNCTION_ARGS)
/* make signature, if array is too long */
if (VARSIZE(res) > TOAST_INDEX_TARGET)
{
- SignTSVector *ressign;
+ SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL);
- len = CALCGTSIZE(SIGNKEY, 0);
- ressign = (SignTSVector *) palloc(len);
- SET_VARSIZE(ressign, len);
- ressign->flag = SIGNKEY;
- makesign(GETSIGN(ressign), res);
+ makesign(GETSIGN(ressign), res, siglen);
res = ressign;
}
@@ -226,22 +243,17 @@ gtsvector_compress(PG_FUNCTION_ARGS)
else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
!ISALLTRUE(DatumGetPointer(entry->key)))
{
- int32 i,
- len;
+ int32 i;
SignTSVector *res;
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- len = CALCGTSIZE(SIGNKEY | ALLISTRUE, 0);
- res = (SignTSVector *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = SIGNKEY | ALLISTRUE;
-
+ res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
entry->rel, entry->page,
@@ -315,12 +327,14 @@ checkcondition_arr(void *checkval, QueryOperand *val, ExecPhraseData *data)
static bool
checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data)
{
+ void *key = (SignTSVector *) checkval;
+
/*
* we are not able to find a prefix in signature tree
*/
if (val->prefix)
return true;
- return GETBIT(checkval, HASHVAL(val->valcrc));
+ return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key)));
}
Datum
@@ -347,7 +361,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
/* since signature is lossy, cannot specify CALC_NOT here */
PG_RETURN_BOOL(TS_execute(GETQUERY(query),
- (void *) GETSIGN(key),
+ key,
TS_EXEC_PHRASE_NO_POS,
checkcondition_bit));
}
@@ -365,7 +379,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
{
int32 i;
@@ -376,7 +390,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ Assert(GETSIGLEN(add) == siglen);
+
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
@@ -384,7 +400,7 @@ unionkey(BITVECP sbase, SignTSVector *add)
int32 *ptr = GETARR(add);
for (i = 0; i < ARRNELEM(add); i++)
- HASH(sbase, ptr[i]);
+ HASH(sbase, ptr[i], siglen);
}
return 0;
}
@@ -395,30 +411,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- SignTSVector *result;
+ int siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
+ SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+ BITVECP base = GETSIGN(result);
+ int32 i;
+
+ memset(base, 0, siglen);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag |= ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
break;
}
}
- flag |= SIGNKEY;
- len = CALCGTSIZE(flag, 0);
- result = (SignTSVector *) palloc(len);
- *size = len;
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -429,6 +439,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
if (ISSIGNKEY(a))
{ /* then b also ISSIGNKEY */
@@ -444,8 +455,10 @@ gtsvector_same(PG_FUNCTION_ARGS)
BITVECP sa = GETSIGN(a),
sb = GETSIGN(b);
+ Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen);
+
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -482,19 +495,19 @@ gtsvector_same(PG_FUNCTION_ARGS)
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
- return pg_popcount(sign, SIGLEN);
+ return pg_popcount(sign, siglen);
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
diff,
dist = 0;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
diff = (unsigned char) (a[i] ^ b[i]);
/* Using the popcount functions here isn't likely to win */
@@ -506,17 +519,22 @@ hemdistsign(BITVECP a, BITVECP b)
static int
hemdist(SignTSVector *a, SignTSVector *b)
{
+ int siglena = GETSIGLEN(a);
+ int siglenb = GETSIGLEN(b);
+
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
+
+ Assert(siglena == siglenb);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
}
Datum
@@ -525,6 +543,7 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
BITVECP orig = GETSIGN(origval);
@@ -533,14 +552,22 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
if (ISARRKEY(newval))
{
- BITVEC sign;
+ BITVECP sign = palloc(siglen);
- makesign(sign, newval);
+ makesign(sign, newval, siglen);
if (ISALLTRUE(origval))
- *penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+ {
+ int siglenbit = SIGLENBIT(siglen);
+
+ *penalty =
+ (float) (siglenbit - sizebitvec(sign, siglen)) /
+ (float) (siglenbit + 1);
+ }
else
- *penalty = hemdistsign(sign, orig);
+ *penalty = hemdistsign(sign, orig, siglen);
+
+ pfree(sign);
}
else
*penalty = hemdist(origval, newval);
@@ -550,19 +577,19 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
typedef struct
{
bool allistrue;
- BITVEC sign;
+ BITVECP sign;
} CACHESIGN;
static void
-fillcache(CACHESIGN *item, SignTSVector *key)
+fillcache(CACHESIGN *item, SignTSVector *key, int siglen)
{
item->allistrue = false;
if (ISARRKEY(key))
- makesign(item->sign, key);
+ makesign(item->sign, key, siglen);
else if (ISALLTRUE(key))
item->allistrue = true;
else
- memcpy((void *) item->sign, (void *) GETSIGN(key), sizeof(BITVEC));
+ memcpy((void *) item->sign, (void *) GETSIGN(key), siglen);
}
#define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
@@ -586,19 +613,19 @@ comparecost(const void *va, const void *vb)
static int
-hemdistcache(CACHESIGN *a, CACHESIGN *b)
+hemdistcache(CACHESIGN *a, CACHESIGN *b, int siglen)
{
if (a->allistrue)
{
if (b->allistrue)
return 0;
else
- return SIGLENBIT - sizebitvec(b->sign);
+ return SIGLENBIT(siglen) - sizebitvec(b->sign, siglen);
}
else if (b->allistrue)
- return SIGLENBIT - sizebitvec(a->sign);
+ return SIGLENBIT(siglen) - sizebitvec(a->sign, siglen);
- return hemdistsign(a->sign, b->sign);
+ return hemdistsign(a->sign, b->sign, siglen);
}
Datum
@@ -606,6 +633,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ int siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
OffsetNumber k,
j;
SignTSVector *datum_l,
@@ -625,6 +653,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
BITVECP ptr;
int i;
CACHESIGN *cache;
+ char *cache_sign;
SPLITCOST *costvector;
maxoff = entryvec->n - 2;
@@ -633,16 +662,22 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
v->spl_right = (OffsetNumber *) palloc(nbytes);
cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
- fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber));
+ cache_sign = palloc(siglen * (maxoff + 2));
+
+ for (j = 0; j < maxoff + 2; j++)
+ cache[j].sign = &cache_sign[siglen * j];
+
+ fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber),
+ siglen);
for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
{
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
if (k == FirstOffsetNumber)
- fillcache(&cache[j], GETENTRY(entryvec, j));
+ fillcache(&cache[j], GETENTRY(entryvec, j), siglen);
- size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+ size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -664,44 +699,21 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (cache[seed_1].allistrue)
- {
- datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_l->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY, 0));
- datum_l->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_l), (void *) cache[seed_1].sign, sizeof(BITVEC));
- }
- if (cache[seed_2].allistrue)
- {
- datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_r->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY, 0));
- datum_r->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_r), (void *) cache[seed_2].sign, sizeof(BITVEC));
- }
-
+ datum_l = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0),
+ siglen, cache[seed_1].sign);
+ datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0),
+ siglen, cache[seed_2].sign);
union_l = GETSIGN(datum_l);
union_r = GETSIGN(datum_r);
maxoff = OffsetNumberNext(maxoff);
- fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff));
+ fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff), siglen);
/* sort before ... */
costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff);
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
{
costvector[j - 1].pos = j;
- size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]));
- size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]));
+ size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen);
+ size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -727,36 +739,34 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_l) && cache[j].allistrue)
size_alpha = 0;
else
- size_alpha = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign)
- );
+ size_alpha = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign), siglen);
}
else
- size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l));
+ size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (ISALLTRUE(datum_r) && cache[j].allistrue)
size_beta = 0;
else
- size_beta = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign)
- );
+ size_beta = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign), siglen);
}
else
- size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r));
+ size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
{
if (ISALLTRUE(datum_l) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_l), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -767,12 +777,12 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_r))
- MemSet((void *) GETSIGN(datum_r), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_r), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -799,3 +809,17 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
{
return gtsvector_consistent(fcinfo);
}
+
+Datum
+gtsvector_options(PG_FUNCTION_ARGS)
+{
+ local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+ GistTsVectorOptions *options = NULL;
+
+ extend_local_reloptions(relopts, options, sizeof(*options));
+ add_local_int_reloption(relopts, "siglen", "signature length",
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX,
+ &options->siglen);
+
+ PG_RETURN_VOID();
+}
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 5e70501..53a64ed 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -481,6 +481,9 @@
amproc => 'gtsvector_picksplit' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '7', amproc => 'gtsvector_same' },
+{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
+ amprocrighttype => 'tsvector', amprocnum => '0',
+ amproc => 'gtsvector_options' },
{ amprocfamily => 'gist/tsquery_ops', amproclefttype => 'tsquery',
amprocrighttype => 'tsquery', amprocnum => '1',
amproc => 'gtsquery_consistent(internal,tsquery,int2,oid,internal)' },
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index e6645f1..ac4440b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8646,6 +8646,9 @@
proname => 'gtsvector_consistent', prorettype => 'bool',
proargtypes => 'internal gtsvector int4 oid internal',
prosrc => 'gtsvector_consistent_oldsig' },
+{ oid => '3434', descr => 'GiST tsvector support',
+ proname => 'gtsvector_options', prorettype => 'void', proisstrict => 'f',
+ proargtypes => 'internal', prosrc => 'gtsvector_options' },
{ oid => '3656', descr => 'GIN tsvector support',
proname => 'gin_extract_tsvector', prorettype => 'internal',
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index 7af2899..ff8ec8e 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
508
(1 row)
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR: value 0 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+ERROR: value 485 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR: parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a)
+ "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx2
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a tsvector_ops (siglen='484'))
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index ece80b9..1bdbfd8 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+
+\d test_tsvector
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
--
2.7.4
[text/x-patch] 0005-Remove-pg_index.indoption-20190910.patch (39.9K, ../dcb740af-2a93-8785-eb26-a4273029f2fb@postgrespro.ru/7-0005-Remove-pg_index.indoption-20190910.patch)
download | inline diff:
From da324f0e2aae09f185ceca3473e8ca8eeae3c317 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:40 +0300
Subject: [PATCH 5/5] Remove pg_index.indoption
---
doc/src/sgml/catalogs.sgml | 11 -------
src/backend/access/common/reloptions.c | 37 +++++++++++++++++++++
src/backend/access/nbtree/nbtree.c | 3 +-
src/backend/access/nbtree/nbtutils.c | 36 +++++++++++++--------
src/backend/catalog/index.c | 19 ++---------
src/backend/catalog/toasting.c | 6 +---
src/backend/commands/indexcmds.c | 51 ++++++++++++++---------------
src/backend/optimizer/util/plancat.c | 16 ++++++----
src/backend/parser/parse_utilcmd.c | 26 +--------------
src/backend/utils/adt/amutils.c | 45 ++++++++++++++------------
src/backend/utils/adt/ruleutils.c | 53 ++++++++++++++++++-------------
src/backend/utils/cache/relcache.c | 41 +++++-------------------
src/include/access/genam.h | 7 ++++
src/include/access/nbtree.h | 11 +++----
src/include/access/reloptions.h | 3 ++
src/include/catalog/index.h | 1 -
src/include/catalog/pg_index.h | 9 ++----
src/include/utils/rel.h | 1 -
src/test/regress/expected/btree_index.out | 4 +--
src/test/regress/expected/opr_sanity.out | 3 +-
src/test/regress/sql/opr_sanity.sql | 3 +-
21 files changed, 183 insertions(+), 203 deletions(-)
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 5e71a2e..5b95131 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3916,17 +3916,6 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</row>
<row>
- <entry><structfield>indoption</structfield></entry>
- <entry><type>int2vector</type></entry>
- <entry></entry>
- <entry>
- This is an array of <structfield>indnkeyatts</structfield> values that
- store per-column flag bits. The meaning of the bits is defined by
- the index's access method.
- </entry>
- </row>
-
- <row>
<entry><structfield>indexprs</structfield></entry>
<entry><type>pg_node_tree</type></entry>
<entry></entry>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 3ad1324..5d7e704 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1888,3 +1888,40 @@ AlterTableGetRelOptionsLockLevel(List *defList)
return lockmode;
}
+
+static void
+ordattoptions_validator(void *parsed_options, relopt_value *vals, int nvals)
+{
+ OrderedAttOptions *ordopts = parsed_options;
+
+ for (int i = 0; i < nvals; i++)
+ if (!strcmp(vals[i].gen->name, "nulls_first") &&
+ !vals[i].isset)
+ ordopts->nulls_first = ordopts->desc;
+}
+
+void
+ordered_index_attoptions(local_relopts *relopts, int attno)
+{
+ OrderedAttOptions *attopts = NULL;
+
+ init_local_reloptions(relopts, attopts, sizeof(*attopts));
+ add_local_bool_reloption(relopts, INDOPTION_DESC,
+ "Descending ordering",
+ false, &attopts->desc);
+ add_local_bool_reloption(relopts, INDOPTION_NULLS_FIRST,
+ "NULLs sorted first",
+ false, &attopts->nulls_first);
+ register_reloptions_validator(relopts, ordattoptions_validator);
+}
+
+OrderedAttOptions *
+get_ordered_attoptions(Datum attoptions)
+{
+ local_relopts relopts;
+
+ ordered_index_attoptions(&relopts, 0);
+
+ return (OrderedAttOptions *)
+ parseAndFillLocalRelOptions(&relopts, attoptions, false);
+}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 0741107..d4ed36d 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -20,6 +20,7 @@
#include "access/nbtree.h"
#include "access/nbtxlog.h"
+#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/xlog.h"
#include "commands/progress.h"
@@ -133,7 +134,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
amroutine->amoptions = btoptions;
- amroutine->amattoptions = NULL;
+ amroutine->amattoptions = ordered_index_attoptions;
amroutine->amproperty = btproperty;
amroutine->ambuildphasename = btbuildphasename;
amroutine->amvalidate = btvalidate;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 4c7b2d0..8d093de 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -46,7 +46,7 @@ static int _bt_compare_array_elements(const void *a, const void *b, void *arg);
static bool _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
ScanKey leftarg, ScanKey rightarg,
bool *result);
-static bool _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption);
+static bool _bt_fix_scankey_strategy(ScanKey skey, bytea **attoptions);
static void _bt_mark_scankey_required(ScanKey skey);
static bool _bt_check_rowcompare(ScanKey skey,
IndexTuple tuple, int tupnatts, TupleDesc tupdesc,
@@ -54,6 +54,9 @@ static bool _bt_check_rowcompare(ScanKey skey,
static int _bt_keep_natts(Relation rel, IndexTuple lastleft,
IndexTuple firstright, BTScanInsert itup_key);
+#define _bt_scankey_ordering_flags(ordopts) \
+ ((ordopts->desc ? SK_BT_DESC : 0) | \
+ (ordopts->nulls_first ? SK_BT_NULLS_FIRST : 0))
/*
* _bt_mkscankey
@@ -88,13 +91,13 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
ScanKey skey;
TupleDesc itupdesc;
int indnkeyatts;
- int16 *indoption;
+ bytea **attoptions;
int tupnatts;
int i;
itupdesc = RelationGetDescr(rel);
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(rel);
- indoption = rel->rd_indoption;
+ attoptions = RelationGetIndexAttOptions(rel, false);
tupnatts = itup ? BTreeTupleGetNAtts(itup, rel) : 0;
Assert(tupnatts <= IndexRelationGetNumberOfAttributes(rel));
@@ -117,6 +120,7 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
for (i = 0; i < indnkeyatts; i++)
{
FmgrInfo *procinfo;
+ OrderedAttOptions *ordopts = (OrderedAttOptions *) attoptions[i];
Datum arg;
bool null;
int flags;
@@ -139,7 +143,9 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
arg = (Datum) 0;
null = true;
}
- flags = (null ? SK_ISNULL : 0) | (indoption[i] << SK_BT_INDOPTION_SHIFT);
+
+ flags = (null ? SK_ISNULL : 0) | _bt_scankey_ordering_flags(ordopts);
+
ScanKeyEntryInitializeWithInfo(&skey[i],
flags,
(AttrNumber) (i + 1),
@@ -192,7 +198,7 @@ _bt_preprocess_array_keys(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
int numberOfKeys = scan->numberOfKeys;
- int16 *indoption = scan->indexRelation->rd_indoption;
+ bytea **attopts = RelationGetIndexAttOptions(scan->indexRelation, false);
int numArrayKeys;
ScanKey cur;
int i;
@@ -335,7 +341,7 @@ _bt_preprocess_array_keys(IndexScanDesc scan)
* successive primitive indexscans produce data in index order.
*/
num_elems = _bt_sort_array_elements(scan, cur,
- (indoption[cur->sk_attno - 1] & INDOPTION_DESC) != 0,
+ ((OrderedAttOptions *) attopts[cur->sk_attno - 1])->desc,
elem_values, num_nonnulls);
/*
@@ -745,7 +751,7 @@ _bt_preprocess_keys(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
int numberOfKeys = scan->numberOfKeys;
- int16 *indoption = scan->indexRelation->rd_indoption;
+ bytea **attoptions = RelationGetIndexAttOptions(scan->indexRelation, false);
int new_numberOfKeys;
int numberOfEqualCols;
ScanKey inkeys;
@@ -782,7 +788,7 @@ _bt_preprocess_keys(IndexScanDesc scan)
if (numberOfKeys == 1)
{
/* Apply indoption to scankey (might change sk_strategy!) */
- if (!_bt_fix_scankey_strategy(cur, indoption))
+ if (!_bt_fix_scankey_strategy(cur, attoptions))
so->qual_ok = false;
memcpy(outkeys, cur, sizeof(ScanKeyData));
so->numberOfKeys = 1;
@@ -817,7 +823,7 @@ _bt_preprocess_keys(IndexScanDesc scan)
if (i < numberOfKeys)
{
/* Apply indoption to scankey (might change sk_strategy!) */
- if (!_bt_fix_scankey_strategy(cur, indoption))
+ if (!_bt_fix_scankey_strategy(cur, attoptions))
{
/* NULL can't be matched, so give up */
so->qual_ok = false;
@@ -1195,11 +1201,10 @@ _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
* not going to change while the scankey survives.
*/
static bool
-_bt_fix_scankey_strategy(ScanKey skey, int16 *indoption)
+_bt_fix_scankey_strategy(ScanKey skey, bytea **attopts)
{
- int addflags;
-
- addflags = indoption[skey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
+ OrderedAttOptions *ordopts = (OrderedAttOptions *) attopts[skey->sk_attno - 1];
+ int addflags = _bt_scankey_ordering_flags(ordopts);
/*
* We treat all btree operators as strict (even if they're not so marked
@@ -1268,7 +1273,10 @@ _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption)
for (;;)
{
Assert(subkey->sk_flags & SK_ROW_MEMBER);
- addflags = indoption[subkey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
+
+ ordopts = (OrderedAttOptions *) attopts[subkey->sk_attno - 1];
+ addflags = _bt_scankey_ordering_flags(ordopts);
+
if ((addflags & SK_BT_DESC) && !(subkey->sk_flags & SK_BT_DESC))
subkey->sk_strategy = BTCommuteStrategyNumber(subkey->sk_strategy);
subkey->sk_flags |= addflags;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index cb7f0ba..60ec7ab 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -114,7 +114,6 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
IndexInfo *indexInfo,
Oid *collationOids,
Oid *classOids,
- int16 *coloptions,
bool primary,
bool isexclusion,
bool immediate,
@@ -535,7 +534,6 @@ UpdateIndexRelation(Oid indexoid,
IndexInfo *indexInfo,
Oid *collationOids,
Oid *classOids,
- int16 *coloptions,
bool primary,
bool isexclusion,
bool immediate,
@@ -545,7 +543,6 @@ UpdateIndexRelation(Oid indexoid,
int2vector *indkey;
oidvector *indcollation;
oidvector *indclass;
- int2vector *indoption;
Datum exprsDatum;
Datum predDatum;
Datum values[Natts_pg_index];
@@ -555,7 +552,7 @@ UpdateIndexRelation(Oid indexoid,
int i;
/*
- * Copy the index key, opclass, and indoption info into arrays (should we
+ * Copy the index key and opclass info into arrays (should we
* make the caller pass them like this to start with?)
*/
indkey = buildint2vector(NULL, indexInfo->ii_NumIndexAttrs);
@@ -563,7 +560,6 @@ UpdateIndexRelation(Oid indexoid,
indkey->values[i] = indexInfo->ii_IndexAttrNumbers[i];
indcollation = buildoidvector(collationOids, indexInfo->ii_NumIndexKeyAttrs);
indclass = buildoidvector(classOids, indexInfo->ii_NumIndexKeyAttrs);
- indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexKeyAttrs);
/*
* Convert the index expressions (if any) to a text datum
@@ -622,7 +618,6 @@ UpdateIndexRelation(Oid indexoid,
values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey);
values[Anum_pg_index_indcollation - 1] = PointerGetDatum(indcollation);
values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass);
- values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption);
values[Anum_pg_index_indexprs - 1] = exprsDatum;
if (exprsDatum == (Datum) 0)
nulls[Anum_pg_index_indexprs - 1] = true;
@@ -665,7 +660,6 @@ UpdateIndexRelation(Oid indexoid,
* tableSpaceId: OID of tablespace to use
* collationObjectId: array of collation OIDs, one per index column
* classObjectId: array of index opclass OIDs, one per index column
- * coloptions: array of per-index-column indoption settings
* reloptions: AM-specific options
* flags: bitmask that can include any combination of these bits:
* INDEX_CREATE_IS_PRIMARY
@@ -705,7 +699,6 @@ index_create(Relation heapRelation,
Oid tableSpaceId,
Oid *collationObjectId,
Oid *classObjectId,
- int16 *coloptions,
Datum reloptions,
bits16 flags,
bits16 constr_flags,
@@ -950,7 +943,7 @@ index_create(Relation heapRelation,
*/
UpdateIndexRelation(indexRelationId, heapRelationId, parentIndexRelid,
indexInfo,
- collationObjectId, classObjectId, coloptions,
+ collationObjectId, classObjectId,
isprimary, is_exclusion,
(constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) == 0,
!concurrent && !invalid,
@@ -1215,10 +1208,8 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
HeapTuple indexTuple,
classTuple;
Datum indclassDatum,
- colOptionDatum,
optionDatum;
oidvector *indclass;
- int2vector *indcoloptions;
bool isnull;
List *indexColNames = NIL;
List *indexExprs = NIL;
@@ -1247,11 +1238,6 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
Assert(!isnull);
indclass = (oidvector *) DatumGetPointer(indclassDatum);
- colOptionDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
- Anum_pg_index_indoption, &isnull);
- Assert(!isnull);
- indcoloptions = (int2vector *) DatumGetPointer(colOptionDatum);
-
/* Fetch options of index if any */
classTuple = SearchSysCache1(RELOID, oldIndexId);
if (!HeapTupleIsValid(classTuple))
@@ -1339,7 +1325,6 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
indexRelation->rd_rel->reltablespace,
indexRelation->rd_indcollation,
indclass->values,
- indcoloptions->values,
optionDatum,
INDEX_CREATE_SKIP_BUILD | INDEX_CREATE_CONCURRENT,
0,
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 7290731..5361955 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -142,7 +142,6 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
IndexInfo *indexInfo;
Oid collationObjectId[2];
Oid classObjectId[2];
- int16 coloptions[2];
ObjectAddress baseobject,
toastobject;
@@ -320,16 +319,13 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
classObjectId[0] = OID_BTREE_OPS_OID;
classObjectId[1] = INT4_BTREE_OPS_OID;
- coloptions[0] = 0;
- coloptions[1] = 0;
-
index_create(toast_rel, toast_idxname, toastIndexOid, InvalidOid,
InvalidOid, InvalidOid,
indexInfo,
list_make2("chunk_id", "chunk_seq"),
BTREE_AM_OID,
rel->rd_rel->reltablespace,
- collationObjectId, classObjectId, coloptions, (Datum) 0,
+ collationObjectId, classObjectId, (Datum) 0,
INDEX_CREATE_IS_PRIMARY, 0, true, true, NULL);
table_close(toast_rel, NoLock);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 04148e5..1e08100 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -73,7 +73,6 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
Oid *typeOidP,
Oid *collationOidP,
Oid *classOidP,
- int16 *colOptionP,
List *attList,
List *exclusionOpNames,
Oid relId,
@@ -155,7 +154,6 @@ CheckIndexCompatible(Oid oldId,
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine;
bool amcanorder;
- int16 *coloptions;
IndexInfo *indexInfo;
int numberOfAttributes;
int old_natts;
@@ -208,11 +206,9 @@ CheckIndexCompatible(Oid oldId,
typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
- coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
ComputeIndexAttrs(indexInfo,
typeObjectId, collationObjectId, classObjectId,
- coloptions, attributeList,
- exclusionOpNames, relationId,
+ attributeList, exclusionOpNames, relationId,
accessMethodName, accessMethodId,
amcanorder, isconstraint);
@@ -504,7 +500,6 @@ DefineIndex(Oid relationId,
amoptions_function amoptions;
bool partitioned;
Datum reloptions;
- int16 *coloptions;
IndexInfo *indexInfo;
bits16 flags;
bits16 constr_flags;
@@ -834,11 +829,9 @@ DefineIndex(Oid relationId,
typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
- coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
ComputeIndexAttrs(indexInfo,
typeObjectId, collationObjectId, classObjectId,
- coloptions, allIndexParams,
- stmt->excludeOpNames, relationId,
+ allIndexParams, stmt->excludeOpNames, relationId,
accessMethodName, accessMethodId,
amcanorder, stmt->isconstraint);
@@ -1036,7 +1029,7 @@ DefineIndex(Oid relationId,
stmt->oldNode, indexInfo, indexColNames,
accessMethodId, tablespaceId,
collationObjectId, classObjectId,
- coloptions, reloptions,
+ reloptions,
flags, constr_flags,
allowSystemTableMods, !check_rights,
&createdConstraintId);
@@ -1566,7 +1559,6 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Oid *typeOidP,
Oid *collationOidP,
Oid *classOidP,
- int16 *colOptionP,
List *attList, /* list of IndexElem's */
List *exclusionOpNames,
Oid relId,
@@ -1714,7 +1706,6 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
errmsg("including column does not support NULLS FIRST/LAST options")));
classOidP[attn] = InvalidOid;
- colOptionP[attn] = 0;
collationOidP[attn] = InvalidOid;
attn++;
@@ -1829,22 +1820,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
* zero for any un-ordered index, while ordered indexes have DESC and
* NULLS FIRST/LAST options.
*/
- colOptionP[attn] = 0;
- if (amcanorder)
- {
- /* default ordering is ASC */
- if (attribute->ordering == SORTBY_DESC)
- colOptionP[attn] |= INDOPTION_DESC;
- /* default null ordering is LAST for ASC, FIRST for DESC */
- if (attribute->nulls_ordering == SORTBY_NULLS_DEFAULT)
- {
- if (attribute->ordering == SORTBY_DESC)
- colOptionP[attn] |= INDOPTION_NULLS_FIRST;
- }
- else if (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
- colOptionP[attn] |= INDOPTION_NULLS_FIRST;
- }
- else
+ if (!amcanorder)
{
/* index AM does not support ordering */
if (attribute->ordering != SORTBY_DEFAULT)
@@ -1859,6 +1835,25 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
accessMethodName)));
}
+ if (attribute->ordering == SORTBY_DESC)
+ {
+ attribute->opclassopts =
+ lappend(attribute->opclassopts,
+ makeDefElem(INDOPTION_DESC, NULL, -1));
+
+ if (attribute->nulls_ordering == SORTBY_NULLS_LAST)
+ attribute->opclassopts =
+ lappend(attribute->opclassopts,
+ makeDefElem(INDOPTION_NULLS_FIRST,
+ (Node *) makeString("false"), -1));
+ }
+ else if (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
+ {
+ attribute->opclassopts =
+ lappend(attribute->opclassopts,
+ makeDefElem(INDOPTION_NULLS_FIRST, NULL, -1));
+ }
+
/* Set up the per-column opclass options (attoptions field). */
if (attribute->opclassopts)
{
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index de9b6f4..6845079 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -299,10 +299,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
for (i = 0; i < nkeycolumns; i++)
{
- int16 opt = indexRelation->rd_indoption[i];
+ OrderedAttOptions *opt = (OrderedAttOptions *) info->opclassoptions[i];
- info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
- info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
+ Assert(opt);
+
+ info->reverse_sort[i] = opt->desc;
+ info->nulls_first[i] = opt->nulls_first;
}
}
else if (amroutine->amcanorder)
@@ -327,14 +329,16 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
for (i = 0; i < nkeycolumns; i++)
{
- int16 opt = indexRelation->rd_indoption[i];
+ OrderedAttOptions *opt = (OrderedAttOptions *) info->opclassoptions[i];
Oid ltopr;
Oid btopfamily;
Oid btopcintype;
int16 btstrategy;
- info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
- info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
+ Assert(opt);
+
+ info->reverse_sort[i] = opt->desc;
+ info->nulls_first[i] = opt->nulls_first;
ltopr = get_opfamily_member(info->opfamily[i],
info->opcintype[i],
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 5872376..8badc74 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1523,7 +1523,6 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
AttrNumber attnum = idxrec->indkey.values[keyno];
Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(source_idx),
keyno);
- int16 opt = source_idx->rd_indoption[keyno];
iparam = makeNode(IndexElem);
@@ -1583,28 +1582,6 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
iparam->ordering = SORTBY_DEFAULT;
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
- /* Adjust options if necessary */
- if (source_idx->rd_indam->amcanorder)
- {
- /*
- * If it supports sort ordering, copy DESC and NULLS opts. Don't
- * set non-default settings unnecessarily, though, so as to
- * improve the chance of recognizing equivalence to constraint
- * indexes.
- */
- if (opt & INDOPTION_DESC)
- {
- iparam->ordering = SORTBY_DESC;
- if ((opt & INDOPTION_NULLS_FIRST) == 0)
- iparam->nulls_ordering = SORTBY_NULLS_LAST;
- }
- else
- {
- if (opt & INDOPTION_NULLS_FIRST)
- iparam->nulls_ordering = SORTBY_NULLS_FIRST;
- }
- }
-
index->indexParams = lappend(index->indexParams, iparam);
}
@@ -2161,8 +2138,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
defopclass = GetDefaultOpClass(attform->atttypid,
index_rel->rd_rel->relam);
if (indclass->values[i] != defopclass ||
- attoptions != (Datum) 0 ||
- index_rel->rd_indoption[i] != 0)
+ attoptions != (Datum) 0)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("index \"%s\" column number %d does not have default sorting behavior", index_name, i + 1),
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index e81d6cc..932f60c 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -15,9 +15,11 @@
#include "access/amapi.h"
#include "access/htup_details.h"
+#include "access/reloptions.h"
#include "catalog/pg_class.h"
#include "catalog/pg_index.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -114,14 +116,11 @@ lookup_prop_name(const char *name)
* otherwise sets *res to the boolean value to return.
*/
static bool
-test_indoption(HeapTuple tuple, int attno, bool guard,
- int16 iopt_mask, int16 iopt_expect,
- bool *res)
+test_indoption(Oid indexrelid, int attno, bool guard,
+ int offset, bool expect, bool *res)
{
- Datum datum;
- bool isnull;
- int2vector *indoption;
- int16 indoption_val;
+ OrderedAttOptions *ordopts;
+ Datum relopts;
if (!guard)
{
@@ -129,14 +128,14 @@ test_indoption(HeapTuple tuple, int attno, bool guard,
return true;
}
- datum = SysCacheGetAttr(INDEXRELID, tuple,
- Anum_pg_index_indoption, &isnull);
- Assert(!isnull);
+ relopts = get_attoptions(indexrelid, attno);
+ ordopts = get_ordered_attoptions(relopts);
- indoption = ((int2vector *) DatumGetPointer(datum));
- indoption_val = indoption->values[attno - 1];
+ *res = *((bool *)((char *) ordopts + offset)) == expect;
- *res = (indoption_val & iopt_mask) == iopt_expect;
+ pfree(ordopts);
+ if (relopts != (Datum) 0)
+ pfree(DatumGetPointer(relopts));
return true;
}
@@ -249,29 +248,33 @@ indexam_property(FunctionCallInfo fcinfo,
{
case AMPROP_ASC:
if (iskey &&
- test_indoption(tuple, attno, routine->amcanorder,
- INDOPTION_DESC, 0, &res))
+ test_indoption(index_oid, attno, routine->amcanorder,
+ offsetof(OrderedAttOptions, desc), false,
+ &res))
isnull = false;
break;
case AMPROP_DESC:
if (iskey &&
- test_indoption(tuple, attno, routine->amcanorder,
- INDOPTION_DESC, INDOPTION_DESC, &res))
+ test_indoption(index_oid, attno, routine->amcanorder,
+ offsetof(OrderedAttOptions, desc), true,
+ &res))
isnull = false;
break;
case AMPROP_NULLS_FIRST:
if (iskey &&
- test_indoption(tuple, attno, routine->amcanorder,
- INDOPTION_NULLS_FIRST, INDOPTION_NULLS_FIRST, &res))
+ test_indoption(index_oid, attno, routine->amcanorder,
+ offsetof(OrderedAttOptions, nulls_first),
+ true, &res))
isnull = false;
break;
case AMPROP_NULLS_LAST:
if (iskey &&
- test_indoption(tuple, attno, routine->amcanorder,
- INDOPTION_NULLS_FIRST, 0, &res))
+ test_indoption(index_oid, attno, routine->amcanorder,
+ offsetof(OrderedAttOptions, nulls_first),
+ false, &res))
isnull = false;
break;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index faecf04..a649e4b 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -22,6 +22,7 @@
#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/relation.h"
+#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/dependency.h"
@@ -469,7 +470,7 @@ static void add_cast_to(StringInfo buf, Oid typid);
static char *generate_qualified_type_name(Oid typid);
static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
-static void get_reloptions(StringInfo buf, Datum reloptions);
+static int get_reloptions(StringInfo buf, Datum reloptions, bool skip_ord_opts);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
@@ -1194,11 +1195,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
int keyno;
Datum indcollDatum;
Datum indclassDatum;
- Datum indoptionDatum;
bool isnull;
oidvector *indcollation;
oidvector *indclass;
- int2vector *indoption;
StringInfoData buf;
char *str;
char *sep;
@@ -1218,7 +1217,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
indrelid = idxrec->indrelid;
Assert(indexrelid == idxrec->indexrelid);
- /* Must get indcollation, indclass, and indoption the hard way */
+ /* Must get indcollation and indclass the hard way */
indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
Anum_pg_index_indcollation, &isnull);
Assert(!isnull);
@@ -1229,11 +1228,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
Assert(!isnull);
indclass = (oidvector *) DatumGetPointer(indclassDatum);
- indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
- Anum_pg_index_indoption, &isnull);
- Assert(!isnull);
- indoption = (int2vector *) DatumGetPointer(indoptionDatum);
-
/*
* Fetch the pg_class tuple of the index relation
*/
@@ -1370,7 +1364,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
if (!attrsOnly && keyno < idxrec->indnkeyatts &&
(!colno || colno == keyno + 1))
{
- int16 opt = indoption->values[keyno];
Oid indcoll = indcollation->values[keyno];
Datum attoptions = get_attoptions(indexrelid, keyno + 1);
bool has_options = attoptions != (Datum) 0;
@@ -1384,27 +1377,32 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
get_opclass_name(indclass->values[keyno],
has_options ? InvalidOid : keycoltype, &buf);
- if (has_options)
+ if (has_options) /* FIXME default opclass */
{
appendStringInfoString(&buf, " (");
- get_reloptions(&buf, attoptions);
- appendStringInfoChar(&buf, ')');
+ if (get_reloptions(&buf, attoptions, amroutine->amcanorder) > 0)
+ appendStringInfoChar(&buf, ')');
+ else
+ buf.len -= 2;
}
/* Add options if relevant */
- if (amroutine->amcanorder)
+ if (amroutine->amcanorder && has_options)
{
+ OrderedAttOptions *ordopts =
+ get_ordered_attoptions(attoptions);
+
/* if it supports sort ordering, report DESC and NULLS opts */
- if (opt & INDOPTION_DESC)
+ if (ordopts->desc)
{
appendStringInfoString(&buf, " DESC");
/* NULLS FIRST is the default in this case */
- if (!(opt & INDOPTION_NULLS_FIRST))
+ if (!ordopts->nulls_first)
appendStringInfoString(&buf, " NULLS LAST");
}
else
{
- if (opt & INDOPTION_NULLS_FIRST)
+ if (ordopts->nulls_first)
appendStringInfoString(&buf, " NULLS FIRST");
}
}
@@ -11213,18 +11211,19 @@ string_to_text(char *str)
/*
* Generate a C string representing a relation options from text[] datum.
*/
-static void
-get_reloptions(StringInfo buf, Datum reloptions)
+static int
+get_reloptions(StringInfo buf, Datum reloptions, bool skip_ord_opts)
{
Datum *options;
int noptions;
int i;
+ int j;
deconstruct_array(DatumGetArrayTypeP(reloptions),
TEXTOID, -1, false, 'i',
&options, NULL, &noptions);
- for (i = 0; i < noptions; i++)
+ for (i = j = 0; i < noptions; i++)
{
char *option = TextDatumGetCString(options[i]);
char *name;
@@ -11245,7 +11244,15 @@ get_reloptions(StringInfo buf, Datum reloptions)
else
value = "";
- if (i > 0)
+ if (skip_ord_opts &&
+ (!strcmp(name, INDOPTION_DESC) ||
+ !strcmp(name, INDOPTION_NULLS_FIRST)))
+ {
+ pfree(option);
+ continue;
+ }
+
+ if (j++ > 0)
appendStringInfoString(buf, ", ");
appendStringInfo(buf, "%s=", quote_identifier(name));
@@ -11264,6 +11271,8 @@ get_reloptions(StringInfo buf, Datum reloptions)
pfree(option);
}
+
+ return j;
}
/*
@@ -11288,7 +11297,7 @@ flatten_reloptions(Oid relid)
StringInfoData buf;
initStringInfo(&buf);
- get_reloptions(&buf, reloptions);
+ get_reloptions(&buf, reloptions, false);
result = buf.data;
}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index d36b31b..ee32087 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1365,11 +1365,9 @@ RelationInitIndexAccessInfo(Relation relation)
Form_pg_am aform;
Datum indcollDatum;
Datum indclassDatum;
- Datum indoptionDatum;
bool isnull;
oidvector *indcoll;
oidvector *indclass;
- int2vector *indoption;
MemoryContext indexcxt;
MemoryContext oldcontext;
int indnatts;
@@ -1454,9 +1452,6 @@ RelationInitIndexAccessInfo(Relation relation)
relation->rd_indcollation = (Oid *)
MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
- relation->rd_indoption = (int16 *)
- MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(int16));
-
/*
* indcollation cannot be referenced directly through the C struct,
* because it comes after the variable-width indkey field. Must extract
@@ -1491,17 +1486,6 @@ RelationInitIndexAccessInfo(Relation relation)
relation->rd_opfamily, relation->rd_opcintype,
amsupport, indnkeyatts);
- /*
- * Similarly extract indoption and copy it to the cache entry
- */
- indoptionDatum = fastgetattr(relation->rd_indextuple,
- Anum_pg_index_indoption,
- GetPgIndexDescriptor(),
- &isnull);
- Assert(!isnull);
- indoption = (int2vector *) DatumGetPointer(indoptionDatum);
- memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16));
-
#if 0
(void) RelationGetIndexAttOptions(relation, false);
#endif
@@ -5232,6 +5216,14 @@ RelationGetIndexAttOptions(Relation relation, bool copy)
if (attoptions != (Datum) 0)
pfree(DatumGetPointer(attoptions));
}
+ else
+ {
+ OrderedAttOptions *opt = palloc0(sizeof(*opt));
+
+ SET_VARSIZE(opt, sizeof(*opt));
+
+ opts[i] = (bytea *) opt;
+ }
}
/* Copy parsed options to the cache. */
@@ -5527,7 +5519,6 @@ load_relcache_init_file(bool shared)
Oid *opcintype;
RegProcedure *support;
int nsupport;
- int16 *indoption;
Oid *indcollation;
/* Count nailed indexes to ensure we have 'em all */
@@ -5604,16 +5595,6 @@ load_relcache_init_file(bool shared)
rel->rd_indcollation = indcollation;
- /* finally, read the vector of indoption values */
- if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
- goto read_failed;
-
- indoption = (int16 *) MemoryContextAlloc(indexcxt, len);
- if (fread(indoption, 1, len, fp) != len)
- goto read_failed;
-
- rel->rd_indoption = indoption;
-
#if 0
/* finally, read the vector of opcoptions values */
rel->rd_opcoptions = (bytea **)
@@ -5659,7 +5640,6 @@ load_relcache_init_file(bool shared)
Assert(rel->rd_opcintype == NULL);
Assert(rel->rd_support == NULL);
Assert(rel->rd_supportinfo == NULL);
- Assert(rel->rd_indoption == NULL);
Assert(rel->rd_indcollation == NULL);
Assert(rel->rd_opcoptions == NULL);
}
@@ -5944,11 +5924,6 @@ write_relcache_init_file(bool shared)
relform->relnatts * sizeof(Oid),
fp);
- /* finally, write the vector of indoption values */
- write_item(rel->rd_indoption,
- relform->relnatts * sizeof(int16),
- fp);
-
#if 0
Assert(rel->rd_opcoptions);
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 778d17c..322577d 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -117,6 +117,13 @@ typedef enum IndexUniqueCheck
UNIQUE_CHECK_EXISTING /* Check if existing tuple is unique */
} IndexUniqueCheck;
+/* Common attribute options for ordered indexes */
+typedef struct OrderedAttOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ bool desc; /* DESCending ordering */
+ bool nulls_first; /* NULLS FIRST/LAST */
+} OrderedAttOptions;
/*
* generalized index_ interface routines (in indexam.c)
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 52eafe6..6901d6e 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -672,13 +672,12 @@ typedef BTScanOpaqueData *BTScanOpaque;
/*
* We use some private sk_flags bits in preprocessed scan keys. We're allowed
* to use bits 16-31 (see skey.h). The uppermost bits are copied from the
- * index's indoption[] array entry for the index attribute.
+ * index's attoptions for the index attribute.
*/
-#define SK_BT_REQFWD 0x00010000 /* required to continue forward scan */
-#define SK_BT_REQBKWD 0x00020000 /* required to continue backward scan */
-#define SK_BT_INDOPTION_SHIFT 24 /* must clear the above bits */
-#define SK_BT_DESC (INDOPTION_DESC << SK_BT_INDOPTION_SHIFT)
-#define SK_BT_NULLS_FIRST (INDOPTION_NULLS_FIRST << SK_BT_INDOPTION_SHIFT)
+#define SK_BT_REQFWD 0x00010000 /* required to continue forward scan */
+#define SK_BT_REQBKWD 0x00020000 /* required to continue backward scan */
+#define SK_BT_DESC 0x00040000
+#define SK_BT_NULLS_FIRST 0x00080000
/*
* Constant definition for progress reporting. Phase numbers must match
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 70238f9..5592da5 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -316,4 +316,7 @@ extern bytea *attribute_reloptions(Datum reloptions, bool validate);
extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+extern void ordered_index_attoptions(local_relopts *relopts, int attno);
+extern OrderedAttOptions *get_ordered_attoptions(Datum attoptions);
+
#endif /* RELOPTIONS_H */
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index 1113d25..b306882 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -64,7 +64,6 @@ extern Oid index_create(Relation heapRelation,
Oid tableSpaceId,
Oid *collationObjectId,
Oid *classObjectId,
- int16 *coloptions,
Datum reloptions,
bits16 flags,
bits16 constr_flags,
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 2438374..28cd920 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -49,7 +49,6 @@ CATALOG(pg_index,2610,IndexRelationId) BKI_SCHEMA_MACRO
#ifdef CATALOG_VARLEN
oidvector indcollation; /* collation identifiers */
oidvector indclass; /* opclass identifiers */
- int2vector indoption; /* per-column flags (AM-specific meanings) */
pg_node_tree indexprs; /* expression trees for index attributes that
* are not simple column references; one for
* each zero entry in indkey[] */
@@ -68,12 +67,10 @@ typedef FormData_pg_index *Form_pg_index;
#ifdef EXPOSE_TO_CLIENT_CODE
/*
- * Index AMs that support ordered scans must support these two indoption
- * bits. Otherwise, the content of the per-column indoption fields is
- * open for future definition.
+ * Index AMs that support ordered scans must support these two reloptions.
*/
-#define INDOPTION_DESC 0x0001 /* values are in reverse order */
-#define INDOPTION_NULLS_FIRST 0x0002 /* NULLs are first instead of last */
+#define INDOPTION_DESC "desc" /* values are in reverse order */
+#define INDOPTION_NULLS_FIRST "nulls_first" /* NULLs are first instead of last */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 552d9eb..54b3642 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -159,7 +159,6 @@ typedef struct RelationData
Oid *rd_opcintype; /* OIDs of opclass declared input data types */
RegProcedure *rd_support; /* OIDs of support procedures */
struct FmgrInfo *rd_supportinfo; /* lookup info for support procedures */
- int16 *rd_indoption; /* per-column AM-specific flags */
List *rd_indexprs; /* index expression trees, if any */
List *rd_indpred; /* index predicate tree, if any */
Oid *rd_exclops; /* OIDs of exclusion operators, if any */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index b4023de..d80d9cd 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -264,6 +264,6 @@ VACUUM delete_test_table;
INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;
-- Test unsupported btree opclass parameters
create index on btree_tall_tbl (id int4_ops(foo=1));
-ERROR: operator class int4_ops has no options
+ERROR: unrecognized parameter "foo"
create index on btree_tall_tbl (id default(foo=1));
-ERROR: operator class int4_ops has no options
+ERROR: unrecognized parameter "foo"
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 1f53a9c..a8329b6 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2096,8 +2096,7 @@ SELECT p1.indexrelid, p1.indrelid
FROM pg_index as p1
WHERE array_lower(indkey, 1) != 0 OR array_upper(indkey, 1) != indnatts-1 OR
array_lower(indclass, 1) != 0 OR array_upper(indclass, 1) != indnatts-1 OR
- array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1 OR
- array_lower(indoption, 1) != 0 OR array_upper(indoption, 1) != indnatts-1;
+ array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1;
indexrelid | indrelid
------------+----------
(0 rows)
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index cee6a40..7dd42a3 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1341,8 +1341,7 @@ SELECT p1.indexrelid, p1.indrelid
FROM pg_index as p1
WHERE array_lower(indkey, 1) != 0 OR array_upper(indkey, 1) != indnatts-1 OR
array_lower(indclass, 1) != 0 OR array_upper(indclass, 1) != indnatts-1 OR
- array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1 OR
- array_lower(indoption, 1) != 0 OR array_upper(indoption, 1) != indnatts-1;
+ array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1;
-- Check that opclasses and collations match the underlying columns.
-- (As written, this test ignores expression indexes.)
--
2.7.4
view thread (34+ messages) latest in thread
Message-ID: <dcb740af-2a93-8785-eb26-a4273029f2fb@postgrespro.ru>
Permalink: ../dcb740af-2a93-8785-eb26-a4273029f2fb@postgrespro.ru/
Also on: postgresql.org/message-id/dcb740af-2a93-8785-eb26-a4273029f2fb@postgrespro.ru
copy link · copy postgr.es · copy git am
reply Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: n.gluhov@postgrespro.ru, alvherre@2ndquadrant.com, tomas.vondra@2ndquadrant.com, robertmhaas@gmail.com, tgl@sss.pgh.pa.us, dhyan@nataraj.su, obartunov@gmail.com
Subject: Re: [PATCH] Opclass parameters
In-Reply-To: <dcb740af-2a93-8785-eb26-a4273029f2fb@postgrespro.ru>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox