0001-opclass-parameters-v01.patch
text/x-patch
Filename: 0001-opclass-parameters-v01.patch
Type: text/x-patch
Part: 0
Message:
[PATCH] Opclass parameters
Patch
Format: unified
Series: patch v1-0001
| File | + | − |
|---|---|---|
| doc/src/sgml/catalogs.sgml | 11 | 0 |
| doc/src/sgml/indices.sgml | 1 | 1 |
| doc/src/sgml/ref/create_index.sgml | 13 | 3 |
| src/backend/access/common/reloptions.c | 100 | 42 |
| src/backend/access/index/indexam.c | 72 | 0 |
| src/backend/catalog/index.c | 60 | 0 |
| src/backend/catalog/toasting.c | 1 | 0 |
| src/backend/commands/indexcmds.c | 13 | 2 |
| src/backend/nodes/copyfuncs.c | 1 | 0 |
| src/backend/nodes/equalfuncs.c | 1 | 0 |
| src/backend/nodes/outfuncs.c | 1 | 0 |
| src/backend/optimizer/util/plancat.c | 4 | 0 |
| src/backend/parser/gram.y | 47 | 25 |
| src/backend/utils/adt/ruleutils.c | 81 | 52 |
| src/backend/utils/cache/relcache.c | 180 | 0 |
| src/include/access/amapi.h | 7 | 0 |
| src/include/access/genam.h | 7 | 0 |
| src/include/access/reloptions.h | 5 | 0 |
| src/include/catalog/pg_index.h | 3 | 1 |
| src/include/nodes/execnodes.h | 2 | 0 |
| src/include/nodes/parsenodes.h | 1 | 0 |
| src/include/nodes/relation.h | 1 | 0 |
| src/include/utils/relcache.h | 4 | 0 |
| src/include/utils/rel.h | 1 | 0 |
| src/include/utils/ruleutils.h | 2 | 0 |
| src/test/regress/expected/btree_index.out | 5 | 0 |
| src/test/regress/sql/btree_index.sql | 4 | 0 |
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 71e20f2..4cfd759 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3874,6 +3874,17 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
partial index.
</entry>
</row>
+
+ <row>
+ <entry><structfield>indoptions</structfield></entry>
+ <entry><type>text[]</type></entry>
+ <entry></entry>
+ <entry>
+ This is an array of <structfield>indnatts</structfield> strings that
+ store per-column opclass options or null. Each array element is a
+ serialized array of column's opclass options in reloptions format or null.
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 0818196..e6c967b 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -983,7 +983,7 @@ CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target)
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 1fd21e1..907b436 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 } ] [, ...] )
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
[ WHERE <replaceable class="parameter">predicate</replaceable> ]
@@ -232,6 +232,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>
@@ -544,8 +553,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/common/reloptions.c b/src/backend/access/common/reloptions.c
index 46276ce..4c72986 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1007,6 +1007,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.
*
@@ -1061,57 +1115,61 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
/* Done if no options */
if (PointerIsValid(DatumGetPointer(options)))
- {
- ArrayType *array = DatumGetArrayTypeP(options);
- Datum *optiondatums;
- int noptions;
+ parseRelOptionsInternal(options, validate, reloptions, numoptions);
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &optiondatums, NULL, &noptions);
+ *numrelopts = numoptions;
+ return reloptions;
+}
- for (i = 0; i < noptions; i++)
- {
- char *text_str = VARDATA(optiondatums[i]);
- int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
- int j;
+/* Parse local unregistered options. */
+relopt_value *
+parseLocalRelOptions(Datum options, bool validate,
+ relopt_gen *optgen[], int nopts)
+{
+ relopt_value *values = palloc(sizeof(*values) * nopts);
+ int i;
- /* Search for a match in reloptions */
- for (j = 0; j < numoptions; j++)
- {
- int kw_len = reloptions[j].gen->namelen;
+ for (i = 0; i < nopts; i++)
+ {
+ values[i].gen = optgen[i];
+ values[i].isset = false;
+ }
- 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 (options != (Datum) 0)
+ parseRelOptionsInternal(options, validate, values, nopts);
- if (j >= numoptions && validate)
- {
- char *s;
- char *p;
+ return values;
+}
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- *p = '\0';
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter \"%s\"", s)));
- }
- }
+/*
+ * 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(Datum options, relopt_gen *optgen[], int offsets[],
+ int noptions, size_t base_size, bool validate)
+{
+ relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+ relopt_value *vals;
+ void *opts;
+ int i;
- /* It's worth avoiding memory leaks in this function */
- pfree(optiondatums);
- if (((void *) array) != DatumGetPointer(options))
- pfree(array);
+ for (i = 0; i < noptions; i++)
+ {
+ elems[i].optname = optgen[i]->name;
+ elems[i].opttype = optgen[i]->type;
+ elems[i].offset = offsets[i];
}
- *numrelopts = numoptions;
- return reloptions;
+ vals = parseLocalRelOptions(options, validate, optgen, noptions);
+ opts = allocateReloptStruct(base_size, vals, noptions);
+ fillRelOptions(opts, base_size, vals, noptions, validate, elems, noptions);
+
+ if (elems)
+ pfree(elems);
+
+ return opts;
}
/*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 91247f0..9f8aad2 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -75,11 +75,14 @@
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/index.h"
+#include "commands/defrem.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"
#include "utils/tqual.h"
@@ -898,3 +901,72 @@ index_getprocinfo(Relation irel,
return locinfo;
}
+
+/*
+ * Parse opclass-specific options for index column.
+ *
+ * amoptions index relation
+ * attnum column number
+ * indoptions options as text[] datum
+ * validate error flag
+ */
+bytea *
+index_opclass_options(Relation relation, AttrNumber attnum, Datum indoptions,
+ bool validate)
+{
+ amopclassoptions_function amopclassoptions =
+ relation->rd_amroutine->amopclassoptions;
+
+ if (!amopclassoptions)
+ {
+ if (validate && PointerIsValid(DatumGetPointer(indoptions)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("access method \"%s\" does not support opclass options ",
+ get_am_name(relation->rd_rel->relam))));
+
+ return NULL;
+ }
+
+ return amopclassoptions(relation, attnum, indoptions, validate);
+}
+
+bytea *
+index_opclass_options_generic(Relation indrel, AttrNumber attnum,
+ uint16 procnum, Datum reloptions, bool validate)
+{
+ Oid procid = index_getprocid(indrel, attnum, procnum);
+ FmgrInfo *procinfo;
+
+ if (!OidIsValid(procid))
+ {
+ StringInfoData opclassname;
+ Oid opclass;
+ Datum indclassDatum;
+ oidvector *indclass;
+ bool isnull;
+
+ if (!DatumGetPointer(reloptions))
+ return NULL;
+
+ indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple,
+ Anum_pg_index_indclass, &isnull);
+ Assert(!isnull);
+ indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
+ opclass = indclass->values[attnum - 1];
+
+ initStringInfo(&opclassname);
+ get_opclass_name(opclass, InvalidOid, &opclassname);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("operator class%s has no options ", opclassname.data)));
+ }
+
+ procinfo = index_getprocinfo(indrel, attnum, procnum);
+
+ return (bytea *) DatumGetPointer(FunctionCall2(procinfo,
+ reloptions,
+ BoolGetDatum(validate)));
+}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 564f206..f74cac1 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -25,6 +25,7 @@
#include "access/amapi.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/sysattr.h"
#include "access/transam.h"
@@ -586,6 +587,7 @@ UpdateIndexRelation(Oid indexoid,
int2vector *indoption;
Datum exprsDatum;
Datum predDatum;
+ Datum indoptionsDatum;
Datum values[Natts_pg_index];
bool nulls[Natts_pg_index];
Relation pg_index;
@@ -632,6 +634,53 @@ UpdateIndexRelation(Oid indexoid,
else
predDatum = (Datum) 0;
+ if (indexInfo->ii_OpclassOptions != NULL)
+ {
+ Datum *vals = palloc(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+ bool *nulls = palloc(sizeof(bool) * indexInfo->ii_NumIndexAttrs);
+ int dims[1];
+ int lbs[1];
+
+ for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+ {
+ Datum options = indexInfo->ii_OpclassOptions[i];
+
+ nulls[i] = options == (Datum) 0;
+
+ if (!nulls[i])
+ {
+ FunctionCallInfoData fcinfo;
+ Datum result;
+ FmgrInfo flinfo = { 0 };
+
+ InitFunctionCallInfoData(fcinfo, &flinfo, 1, InvalidOid, NULL,
+ NULL);
+
+ fcinfo.arg[0] = options;
+ fcinfo.argnull[0] = false;
+
+ flinfo.fn_mcxt = CurrentMemoryContext;
+
+ result = array_out(&fcinfo);
+
+ /* Check for null result, since caller is clearly not expecting one */
+ if (fcinfo.isnull)
+ elog(ERROR, "function %p returned NULL", (void *) array_out);
+
+ vals[i] = CStringGetTextDatum(DatumGetCString(result));
+ }
+ }
+
+ dims[0] = indexInfo->ii_NumIndexAttrs;
+ lbs[0] = 1;
+
+ indoptionsDatum = PointerGetDatum(construct_md_array(vals, nulls, 1,
+ dims, lbs, TEXTOID,
+ -1, false, 'i'));
+ }
+ else
+ indoptionsDatum = (Datum) 0;
+
/*
* open the system catalog index relation
*/
@@ -665,6 +714,9 @@ UpdateIndexRelation(Oid indexoid,
values[Anum_pg_index_indpred - 1] = predDatum;
if (predDatum == (Datum) 0)
nulls[Anum_pg_index_indpred - 1] = true;
+ values[Anum_pg_index_indoptions - 1] = indoptionsDatum;
+ if (indoptionsDatum == (Datum) 0)
+ nulls[Anum_pg_index_indoptions - 1] = true;
tuple = heap_form_tuple(RelationGetDescr(pg_index), values, nulls);
@@ -1140,6 +1192,12 @@ index_create(Relation heapRelation,
else
Assert(indexRelation->rd_indexcxt != NULL);
+ /* Validate opclass-specific options */
+ if (indexInfo->ii_OpclassOptions)
+ for (i = 0; i < indexInfo->ii_NumIndexAttrs; 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
@@ -1777,6 +1835,8 @@ BuildIndexInfo(Relation index)
ii->ii_ExclusionStrats = NULL;
}
+ ii->ii_OpclassOptions = RelationGetRawOpclassOptions(index);
+
/* other info */
ii->ii_Unique = indexStruct->indisunique;
ii->ii_ReadyForInserts = IndexIsReady(indexStruct);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 8bf2698..099fb7d 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -311,6 +311,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 3e48a58..97cf5d9 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -612,6 +612,7 @@ DefineIndex(Oid relationId,
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
+ indexInfo->ii_OpclassOptions = NULL; /* for now */
indexInfo->ii_Unique = stmt->unique;
/* In a concurrent build, mark it not-ready-for-inserts */
indexInfo->ii_ReadyForInserts = !stmt->concurrent;
@@ -1348,12 +1349,11 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
ListCell *nextExclOp;
ListCell *lc;
int attn;
+ int ncols = list_length(attList);
/* Allocate space for exclusion operator info, if needed */
if (exclusionOpNames)
{
- int ncols = list_length(attList);
-
Assert(list_length(exclusionOpNames) == ncols);
indexInfo->ii_ExclusionOps = (Oid *) palloc(sizeof(Oid) * ncols);
indexInfo->ii_ExclusionProcs = (Oid *) palloc(sizeof(Oid) * ncols);
@@ -1594,6 +1594,17 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
accessMethodName)));
}
+ /* Set up the per-column opclass options (indoptions field). */
+ if (attribute->opclassopts)
+ {
+ if (!indexInfo->ii_OpclassOptions)
+ indexInfo->ii_OpclassOptions = palloc0(sizeof(Datum) * ncols);
+
+ indexInfo->ii_OpclassOptions[attn] =
+ transformRelOptions((Datum) 0, attribute->opclassopts,
+ NULL, NULL, false, false);
+ }
+
attn++;
}
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 266a3ef..e089a6a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2807,6 +2807,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 bbffc87..3c484f4 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2545,6 +2545,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/outfuncs.c b/src/backend/nodes/outfuncs.c
index 011d2a3..c4c2e2e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2872,6 +2872,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 60f2171..5e8f1dd 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -353,6 +353,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->nulls_first = NULL;
}
+ /* Fetch index opclass options */
+ info->opclassoptions =
+ RelationGetParsedOpclassOptions(indexRelation);
+
/*
* Fetch the index expressions and predicate, if any. We must
* modify the copies we obtain from the relcache to have the
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index d99f2be..eb780ce 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -483,7 +483,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
@@ -7438,43 +7438,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/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3697466..7e8bd56 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -450,8 +450,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
deparse_context *context);
static void get_tablesample_def(TableSampleClause *tablesample,
deparse_context *context);
-static void get_opclass_name(Oid opclass, Oid actual_datatype,
- StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
static void printSubscripts(ArrayRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
@@ -465,6 +463,7 @@ static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
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 ")
@@ -1180,6 +1179,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
oidvector *indcollation;
oidvector *indclass;
int2vector *indoption;
+ Datum *indoptions = NULL;
StringInfoData buf;
char *str;
char *sep;
@@ -1215,6 +1215,16 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
Assert(!isnull);
indoption = (int2vector *) DatumGetPointer(indoptionDatum);
+ if (!attrsOnly)
+ {
+ Datum indoptionsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+ Anum_pg_index_indoptions,
+ &isnull);
+ if (!isnull)
+ indoptions = ExtractRawOpclassOptions(indoptionsDatum,
+ idxrec->indnatts);
+ }
+
/*
* Fetch the pg_class tuple of the index relation
*/
@@ -1338,6 +1348,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
if (!attrsOnly && (!colno || colno == keyno + 1))
{
Oid indcoll;
+ bool has_options =
+ indoptions && indoptions[keyno] != (Datum) 0;
/* Add collation, if not default for column */
indcoll = indcollation->values[keyno];
@@ -1346,7 +1358,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, indoptions[keyno]);
+ appendStringInfoChar(&buf, ')');
+ }
/* Add options if relevant */
if (amroutine->amcanorder)
@@ -10297,7 +10317,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
* actual_datatype. (If you don't want this behavior, just pass
* InvalidOid for actual_datatype.)
*/
-static void
+void
get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf)
{
@@ -10934,6 +10954,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 *
@@ -10953,56 +11029,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/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1ebf9c4..1556927 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -77,6 +77,7 @@
#include "storage/smgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -5212,6 +5213,185 @@ GetRelationPublicationActions(Relation relation)
}
/*
+ * RelationGetOpclassOptions -- get parsed opclass-specific options for an index
+ */
+bytea **
+RelationGetParsedOpclassOptions(Relation relation)
+{
+ MemoryContext oldcxt;
+ bytea **opts;
+ Datum indoptionsDatum;
+ Datum *indoptions;
+ bool *indoptionsnulls;
+ int indoptionsnum;
+ int i;
+ int ncols = relation->rd_rel->relnatts;
+ bool isnull;
+
+ if (relation->rd_indoptions)
+ {
+ opts = palloc(sizeof(*opts) * ncols);
+
+ for (i = 0; i < ncols; i++)
+ {
+ bytea *opt = relation->rd_indoptions[i];
+
+ opts[i] = !opt ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+ }
+
+ return opts;
+ }
+
+ opts = palloc0(sizeof(*opts) * ncols);
+
+ if (relation->rd_indextuple == NULL ||
+ heap_attisnull(relation->rd_indextuple, Anum_pg_index_indoptions))
+ {
+ indoptions = NULL;
+ indoptionsnulls = NULL;
+ indoptionsnum = 0;
+ }
+ else
+ {
+ indoptionsDatum = heap_getattr(relation->rd_indextuple,
+ Anum_pg_index_indoptions,
+ GetPgIndexDescriptor(),
+ &isnull);
+ Assert(!isnull);
+
+ deconstruct_array(DatumGetArrayTypeP(indoptionsDatum),
+ TEXTOID, -1, false, 'i',
+ &indoptions, &indoptionsnulls, &indoptionsnum);
+
+ Assert(indoptionsnum == ncols);
+ }
+
+ for (i = 0; i < ncols; i++)
+ {
+ Datum options;
+
+ if (i < indoptionsnum && !indoptionsnulls[i])
+ {
+ FunctionCallInfoData fcinfo;
+ FmgrInfo flinfo = { 0 };
+ char *optionsstr = TextDatumGetCString(indoptions[i]);
+
+ InitFunctionCallInfoData(fcinfo, &flinfo, 3, InvalidOid, NULL, NULL);
+
+ fcinfo.arg[0] = CStringGetDatum(optionsstr);
+ fcinfo.argnull[0] = false;
+ fcinfo.arg[1] = Int32GetDatum(TEXTOID);
+ fcinfo.argnull[1] = false;
+ fcinfo.arg[2] = Int32GetDatum(-1);
+ fcinfo.argnull[2] = false;
+
+ flinfo.fn_mcxt = CurrentMemoryContext;
+
+ options = array_in(&fcinfo);
+
+ if (fcinfo.isnull)
+ elog(ERROR, "function %p returned NULL", (void *) array_in);
+
+ pfree(optionsstr);
+ }
+ else
+ {
+ options = PointerGetDatum(NULL);
+ }
+
+ opts[i] = index_opclass_options(relation, i + 1, options, false);
+
+ if (options != PointerGetDatum(NULL))
+ pfree(DatumGetPointer(options));
+ }
+
+ oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+ relation->rd_indoptions = palloc(sizeof(*opts) * ncols);
+
+ for (i = 0; i < ncols; i++)
+ relation->rd_indoptions[i] = !opts[i] ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opts[i]), false, -1));
+
+ MemoryContextSwitchTo(oldcxt);
+
+ return opts;
+}
+
+Datum *
+ExtractRawOpclassOptions(Datum indoptionsDatum, int ncols)
+{
+ Datum *indoptions;
+ bool *indoptionsnulls;
+ int indoptionsnum;
+ int i;
+
+ deconstruct_array(DatumGetArrayTypeP(indoptionsDatum),
+ TEXTOID, -1, false, 'i',
+ &indoptions, &indoptionsnulls, &indoptionsnum);
+
+ Assert(indoptionsnum == ncols);
+
+ for (i = 0; i < indoptionsnum; i++)
+ {
+ if (indoptionsnulls[i])
+ indoptions[i] = PointerGetDatum(NULL);
+ else
+ {
+ FunctionCallInfoData fcinfo;
+ FmgrInfo flinfo = { 0 };
+ char *optionsstr = TextDatumGetCString(indoptions[i]);
+
+ InitFunctionCallInfoData(fcinfo, &flinfo, 3, InvalidOid, NULL,
+ NULL);
+
+ fcinfo.arg[0] = CStringGetDatum(optionsstr);
+ fcinfo.argnull[0] = false;
+ fcinfo.arg[1] = Int32GetDatum(TEXTOID);
+ fcinfo.argnull[1] = false;
+ fcinfo.arg[2] = Int32GetDatum(-1);
+ fcinfo.argnull[2] = false;
+
+ flinfo.fn_mcxt = CurrentMemoryContext;
+
+ indoptions[i] = array_in(&fcinfo);
+
+ /* Check for null result, since caller is clearly not expecting one */
+ if (fcinfo.isnull)
+ elog(ERROR, "function %p returned NULL", (void *) array_in);
+ }
+ }
+
+ return indoptions;
+}
+
+/*
+ * RelationGetIndexOpclassOptions -- get opclass-specific options for an index
+ */
+Datum *
+RelationGetRawOpclassOptions(Relation relation)
+{
+ Datum indoptionsDatum;
+ bool isnull;
+
+ /* Quick exit if there is nothing to do. */
+ if (relation->rd_indextuple == NULL ||
+ heap_attisnull(relation->rd_indextuple, Anum_pg_index_indoptions))
+ return NULL;
+
+ indoptionsDatum = heap_getattr(relation->rd_indextuple,
+ Anum_pg_index_indoptions,
+ GetPgIndexDescriptor(),
+ &isnull);
+ Assert(!isnull);
+
+ return ExtractRawOpclassOptions(indoptionsDatum,
+ relation->rd_rel->relnatts);
+}
+
+
+/*
* Routines to support ereport() reports of relation-related errors
*
* These could have been put into elog.c, but it seems like a module layering
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 8d7bc24..bde93c3 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -102,6 +102,12 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
typedef bytea *(*amoptions_function) (Datum reloptions,
bool validate);
+/* parse column opclass-specific options */
+typedef bytea *(*amopclassoptions_function) (Relation index,
+ AttrNumber colno,
+ Datum indoptions,
+ bool validate);
+
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -203,6 +209,7 @@ typedef struct IndexAmRoutine
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amoptions_function amoptions;
+ amopclassoptions_function amopclassoptions;
amproperty_function amproperty; /* can be NULL */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 24c720b..0d490d6 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -174,6 +174,13 @@ extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
uint16 procnum);
extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
uint16 procnum);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+ Datum indoptions, bool validate);
+extern bytea *index_opclass_options_generic(Relation relation,
+ AttrNumber attnum, uint16 procnum,
+ Datum indoptions, 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 b32c1e9..9bb82d2 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -263,12 +263,17 @@ 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(Datum options, bool validate,
+ relopt_gen **gen, int nelems);
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(Datum options, relopt_gen *optgen[],
+ int offsets[], int noptions, size_t base_size,
+ bool validate);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 057a9f7..c40d009 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -56,6 +56,7 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
* each zero entry in indkey[] */
pg_node_tree indpred; /* expression tree for predicate, if a partial
* index; else NULL */
+ text indoptions[1]; /* opclass options */
#endif
} FormData_pg_index;
@@ -70,7 +71,7 @@ typedef FormData_pg_index *Form_pg_index;
* compiler constants for pg_index
* ----------------
*/
-#define Natts_pg_index 19
+#define Natts_pg_index 20
#define Anum_pg_index_indexrelid 1
#define Anum_pg_index_indrelid 2
#define Anum_pg_index_indnatts 3
@@ -90,6 +91,7 @@ typedef FormData_pg_index *Form_pg_index;
#define Anum_pg_index_indoption 17
#define Anum_pg_index_indexprs 18
#define Anum_pg_index_indpred 19
+#define Anum_pg_index_indoptions 20
/*
* Index AMs that support ordered scans must support these two indoption
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a953820..9e65097 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -129,6 +129,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?
@@ -155,6 +156,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 ac292bc..5a359f4 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -698,6 +698,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/relation.h b/src/include/nodes/relation.h
index db8de2d..795f7f6 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -740,6 +740,7 @@ typedef 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/rel.h b/src/include/utils/rel.h
index aa8add5..739478f 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -191,6 +191,7 @@ typedef struct RelationData
uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */
void *rd_amcache; /* available for use by index AM */
Oid *rd_indcollation; /* OIDs of index collations */
+ bytea **rd_indoptions; /* parsed opclass-specific options */
/*
* foreign-table support
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 8a546ab..8ef5795 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"
@@ -50,6 +51,9 @@ extern Oid RelationGetPrimaryKeyIndex(Relation relation);
extern Oid RelationGetReplicaIndex(Relation relation);
extern List *RelationGetIndexExpressions(Relation relation);
extern List *RelationGetIndexPredicate(Relation relation);
+extern bytea **RelationGetParsedOpclassOptions(Relation relation);
+extern Datum *RelationGetRawOpclassOptions(Relation relation);
+extern Datum *ExtractRawOpclassOptions(Datum indoptionsDatum, int ncols);
typedef enum IndexAttrBitmapKind
{
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 9f9b029..86e6953 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -34,5 +34,7 @@ extern List *select_rtable_names_for_explain(List *rtable,
Bitmapset *rels_used);
extern char *generate_collation_name(Oid collid);
extern char *get_range_partbound_string(List *bound_datums);
+extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf);
+
#endif /* RULEUTILS_H */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 755cd17..5504dd9 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -150,3 +150,8 @@ vacuum btree_tall_tbl;
-- need to insert some rows to cause the fast root page to split.
insert into btree_tall_tbl (id, t)
select g, repeat('x', 100) from generate_series(1, 500) g;
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR: access method "btree" does not support opclass options
+create index on btree_tall_tbl (id default(foo=1));
+ERROR: access method "btree" does not support opclass options
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 65b08c8..b7a4890 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -92,3 +92,7 @@ vacuum btree_tall_tbl;
-- need to insert some rows to cause the fast root page to split.
insert into btree_tall_tbl (id, t)
select g, repeat('x', 100) from generate_series(1, 500) g;
+
+-- 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));