pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feed From: Nikita Glukhov <n.gluhov@postgrespro.ru>
To: PostgreSQL mailing lists <pgsql-hackers@postgresql.org>
Cc: Nikolay Shaplov <dhyan@nataraj.su>
Cc: obartunov@gmail.com
Subject: Re: [PATCH] Opclass parameters
Date: Thu, 15 Nov 2018 18:26:43 +0300
Message-ID: <71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru> (raw )
In-Reply-To: <6382cb29-83d8-90f2-1dbb-b4ac606cbf1f@postgrespro.ru >
References: <d22c3a18-31c7-1879-fc11-4c1ce2f5e5af@postgrespro.ru >
<15077864.qMPJiTemUy@x200m >
<CAF4Au4xedPHJQyN60x+6giMpEyLdKn7jFR85_kzaApy9BptRUw@mail.gmail.com >
<2803786.3sFNWAn8Pn@x200m >
<6382cb29-83d8-90f2-1dbb-b4ac606cbf1f@postgrespro.ru >
On 03.03.2018 1:32, Nikita Glukhov wrote:
> On 02.03.2018 19:12, Nikolay Shaplov wrote:
>> But since it is now "Rejected with feedback", let's wait till autumn.
>
> We don't want to wait that long. But now we only need to сome to an
> agreement
> about CREATE INDEX syntax and where to store the opclass parameters.
Attached 2nd version of the patches. Nothing has changed since March,
this is just a rebased version.
CREATE INDEX syntax and parameters storage method still need discussion.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
[text/x-patch] 0001-Add-opclass-parameters-v02.patch (42.1K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/2-0001-Add-opclass-parameters-v02.patch)
download | inline diff:
From 76513025cf34b2a72c8396a3d868db286fe54dbb Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Wed, 10 Jan 2018 00:46:28 +0300
Subject: [PATCH 1/9] Add opclass parameters
---
doc/src/sgml/catalogs.sgml | 11 ++
doc/src/sgml/indices.sgml | 2 +-
doc/src/sgml/ref/create_index.sgml | 16 ++-
src/backend/access/common/reloptions.c | 142 +++++++++++++++++-------
src/backend/access/index/indexam.c | 72 ++++++++++++
src/backend/catalog/index.c | 60 ++++++++++
src/backend/catalog/toasting.c | 1 +
src/backend/commands/indexcmds.c | 12 ++
src/backend/nodes/copyfuncs.c | 1 +
src/backend/nodes/equalfuncs.c | 1 +
src/backend/nodes/outfuncs.c | 1 +
src/backend/optimizer/util/plancat.c | 4 +
src/backend/parser/gram.y | 72 +++++++-----
src/backend/utils/adt/ruleutils.c | 133 +++++++++++++---------
src/backend/utils/cache/relcache.c | 179 ++++++++++++++++++++++++++++++
src/include/access/amapi.h | 7 ++
src/include/access/genam.h | 6 +
src/include/access/reloptions.h | 5 +
src/include/catalog/pg_index.h | 1 +
src/include/nodes/execnodes.h | 2 +
src/include/nodes/parsenodes.h | 1 +
src/include/nodes/relation.h | 1 +
src/include/utils/rel.h | 1 +
src/include/utils/relcache.h | 4 +
src/include/utils/ruleutils.h | 2 +
src/test/regress/expected/btree_index.out | 5 +
src/test/regress/expected/misc_sanity.out | 3 +-
src/test/regress/sql/btree_index.sql | 4 +
28 files changed, 625 insertions(+), 124 deletions(-)
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 8b7f169..bdba62b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3901,6 +3901,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 df7d16f..8afc93e 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -993,7 +993,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 ad619cd..1946d3b 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>
@@ -607,8 +616,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 db84da0..4820a14 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1025,6 +1025,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.
*
@@ -1079,57 +1133,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 eade540..c03ad0c 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/index.h"
#include "catalog/pg_type.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"
@@ -967,3 +970,72 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
}
}
}
+
+/*
+ * 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 4088286..e186dc6 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/reloptions.h"
#include "access/sysattr.h"
@@ -588,6 +589,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;
@@ -634,6 +636,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
*/
@@ -668,6 +717,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);
@@ -1175,6 +1227,12 @@ 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_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
@@ -1803,6 +1861,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 3baaa08..8d08b6a 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -313,6 +313,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 906d711..75c6013 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -642,6 +642,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;
@@ -1695,6 +1696,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) * nkeycols);
+
+ 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 db49968..ad3ff3c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2856,6 +2856,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 3a084b4..e3dfe6e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2538,6 +2538,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 f0c3965..a23c508 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2931,6 +2931,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 0c88c90..2af7c41 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -361,6 +361,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 2effd51..9584262 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -485,7 +485,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
@@ -7433,43 +7433,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 29884f1..64a4990 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -452,8 +452,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);
@@ -468,6 +466,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 ")
@@ -1197,6 +1196,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
oidvector *indcollation;
oidvector *indclass;
int2vector *indoption;
+ Datum *indoptions = NULL;
StringInfoData buf;
char *str;
char *sep;
@@ -1232,6 +1232,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
*/
@@ -1370,6 +1380,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
(!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];
@@ -1378,7 +1390,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)
@@ -10401,7 +10421,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)
{
@@ -11112,6 +11132,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 *
@@ -11131,56 +11207,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 aecbd4a..0fd7dbe 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5257,6 +5257,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, NULL))
+ {
+ 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, NULL))
+ 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 14526a6..c1da8e9 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -103,6 +103,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,
@@ -212,6 +218,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 534fac7..e558ba3 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -177,6 +177,12 @@ extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
extern void index_store_float8_orderby_distances(IndexScanDesc scan,
Oid *orderByTypes, double *distances,
bool recheckOrderBy);
+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 4022c14..f6d6b43 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -264,12 +264,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 5f72a55..04a5c4c 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -55,6 +55,7 @@ CATALOG(pg_index,2610,IndexRelationId) 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;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1854456..fde9153 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -136,6 +136,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?
@@ -164,6 +165,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 9da8bf2..c715a02 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -699,6 +699,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 6fd2420..fba493e 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -783,6 +783,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 84469f5..2826e5d 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -163,6 +163,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 dbbf41b..7af7bb6 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 0bd48dc..e420f10 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -179,3 +179,8 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
{vacuum_cleanup_index_scale_factor=70.0}
(1 row)
+-- 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/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b..43a9962 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -102,8 +102,9 @@ ORDER BY 1, 2;
pg_class | reloptions | text[]
pg_class | relpartbound | pg_node_tree
pg_index | indexprs | pg_node_tree
+ pg_index | indoptions | text[]
pg_index | indpred | pg_node_tree
pg_largeobject | data | bytea
pg_largeobject_metadata | lomacl | aclitem[]
-(11 rows)
+(12 rows)
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 21171f7..a3f7d9b 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -111,3 +111,7 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
-- Simple ALTER INDEX
alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+
+-- 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));
--
2.7.4
[text/x-patch] 0002-Add-opclass-parameters-to-GiST-v02.patch (13.7K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/3-0002-Add-opclass-parameters-to-GiST-v02.patch)
download | inline diff:
From 8618654d93554f0684b0cf3c89de049d6d29435c Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 15 Jan 2018 18:58:39 +0300
Subject: [PATCH 2/9] Add opclass parameters to GiST
---
doc/src/sgml/xindex.sgml | 5 ++++
src/backend/access/gist/gist.c | 2 ++
src/backend/access/gist/gistget.c | 12 ++++++----
src/backend/access/gist/gistsplit.c | 15 +++++++-----
src/backend/access/gist/gistutil.c | 43 +++++++++++++++++++++++-----------
src/backend/access/gist/gistvalidate.c | 34 +++++++++++++++++----------
src/include/access/gist.h | 3 ++-
src/include/access/gist_private.h | 4 ++++
8 files changed, 81 insertions(+), 37 deletions(-)
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b..8c5b528 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -546,6 +546,11 @@
index-only scans (optional)</entry>
<entry>9</entry>
</row>
+ <row>
+ <entry><function>options</function></entry>
+ <entry>parse opclass-specific options (optional)</entry>
+ <entry>10</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 8a42eff..905cbbf 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -97,6 +97,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amopclassoptions = gistopclassoptions;
PG_RETURN_POINTER(amroutine);
}
@@ -1457,6 +1458,7 @@ initGISTstate(Relation index)
giststate->scanCxt = scanCxt;
giststate->tempCxt = scanCxt; /* caller must change this if needed */
giststate->tupdesc = index->rd_att;
+ giststate->opclassoptions = RelationGetParsedOpclassOptions(index);
for (i = 0; i < index->rd_att->natts; i++)
{
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index e4a3786..9302e68 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -196,6 +196,7 @@ gistindex_keytest(IndexScanDesc scan,
Datum test;
bool recheck;
GISTENTRY de;
+ bytea *options = giststate->opclassoptions[key->sk_attno - 1];
gistdentryinit(giststate, key->sk_attno - 1, &de,
datum, r, page, offset,
@@ -216,13 +217,14 @@ gistindex_keytest(IndexScanDesc scan,
*/
recheck = true;
- test = FunctionCall5Coll(&key->sk_func,
+ test = FunctionCall6Coll(&key->sk_func,
key->sk_collation,
PointerGetDatum(&de),
key->sk_argument,
Int16GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype),
- PointerGetDatum(&recheck));
+ PointerGetDatum(&recheck),
+ PointerGetDatum(options));
if (!DatumGetBool(test))
return false;
@@ -257,6 +259,7 @@ gistindex_keytest(IndexScanDesc scan,
Datum dist;
bool recheck;
GISTENTRY de;
+ bytea *options = giststate->opclassoptions[key->sk_attno - 1];
gistdentryinit(giststate, key->sk_attno - 1, &de,
datum, r, page, offset,
@@ -279,13 +282,14 @@ gistindex_keytest(IndexScanDesc scan,
* about the flag, but are expected to never be lossy.
*/
recheck = false;
- dist = FunctionCall5Coll(&key->sk_func,
+ dist = FunctionCall6Coll(&key->sk_func,
key->sk_collation,
PointerGetDatum(&de),
key->sk_argument,
Int16GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype),
- PointerGetDatum(&recheck));
+ PointerGetDatum(&recheck),
+ PointerGetDatum(options));
*recheck_distances_p |= recheck;
*distance_p = DatumGetFloat8(dist);
}
diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c
index a7038cc..96a9290 100644
--- a/src/backend/access/gist/gistsplit.c
+++ b/src/backend/access/gist/gistsplit.c
@@ -378,18 +378,20 @@ genericPickSplit(GISTSTATE *giststate, GistEntryVector *entryvec, GIST_SPLITVEC
evec->n = v->spl_nleft;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber,
sizeof(GISTENTRY) * evec->n);
- v->spl_ldatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ v->spl_ldatum = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ PointerGetDatum(&nbytes),
+ PointerGetDatum(giststate->opclassoptions[attno]));
evec->n = v->spl_nright;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber + v->spl_nleft,
sizeof(GISTENTRY) * evec->n);
- v->spl_rdatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ v->spl_rdatum = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ PointerGetDatum(&nbytes),
+ PointerGetDatum(giststate->opclassoptions[attno]));
}
/*
@@ -430,10 +432,11 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
* Let the opclass-specific PickSplit method do its thing. Note that at
* this point we know there are no null keys in the entryvec.
*/
- FunctionCall2Coll(&giststate->picksplitFn[attno],
+ FunctionCall3Coll(&giststate->picksplitFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(entryvec),
- PointerGetDatum(sv));
+ PointerGetDatum(sv),
+ PointerGetDatum(giststate->opclassoptions[attno]));
if (sv->spl_nleft == 0 || sv->spl_nright == 0)
{
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 70627e5..f9367f9 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -199,10 +199,11 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
}
/* Make union and store in attr array */
- attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
+ attr[i] = FunctionCall3Coll(&giststate->unionFn[i],
giststate->supportCollation[i],
PointerGetDatum(evec),
- PointerGetDatum(&attrsize));
+ PointerGetDatum(&attrsize),
+ PointerGetDatum(giststate->opclassoptions[i]));
isnull[i] = false;
}
@@ -268,10 +269,11 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
}
*dstisnull = false;
- *dst = FunctionCall2Coll(&giststate->unionFn[attno],
+ *dst = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&dstsize));
+ PointerGetDatum(&dstsize),
+ PointerGetDatum(giststate->opclassoptions[attno]));
}
}
@@ -280,10 +282,11 @@ gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
{
bool result;
- FunctionCall3Coll(&giststate->equalFn[attno],
+ FunctionCall4Coll(&giststate->equalFn[attno],
giststate->supportCollation[attno],
a, b,
- PointerGetDatum(&result));
+ PointerGetDatum(&result),
+ PointerGetDatum(giststate->opclassoptions[attno]));
return result;
}
@@ -556,9 +559,10 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
return;
dep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
+ DatumGetPointer(FunctionCall2Coll(&giststate->decompressFn[nkey],
giststate->supportCollation[nkey],
- PointerGetDatum(e)));
+ PointerGetDatum(e),
+ PointerGetDatum(giststate->opclassoptions[nkey])));
/* decompressFn may just return the given pointer */
if (dep != e)
gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
@@ -593,9 +597,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
/* there may not be a compress function in opclass */
if (OidIsValid(giststate->compressFn[i].fn_oid))
cep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+ DatumGetPointer(FunctionCall2Coll(&giststate->compressFn[i],
giststate->supportCollation[i],
- PointerGetDatum(¢ry)));
+ PointerGetDatum(¢ry),
+ PointerGetDatum(giststate->opclassoptions[i])));
else
cep = ¢ry;
compatt[i] = cep->key;
@@ -624,9 +629,10 @@ gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
fep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
+ DatumGetPointer(FunctionCall2Coll(&giststate->fetchFn[nkey],
giststate->supportCollation[nkey],
- PointerGetDatum(&fentry)));
+ PointerGetDatum(&fentry),
+ PointerGetDatum(giststate->opclassoptions[nkey])));
/* fetchFn set 'key', return it to the caller */
return fep->key;
@@ -694,11 +700,12 @@ gistpenalty(GISTSTATE *giststate, int attno,
if (giststate->penaltyFn[attno].fn_strict == false ||
(isNullOrig == false && isNullAdd == false))
{
- FunctionCall3Coll(&giststate->penaltyFn[attno],
+ FunctionCall4Coll(&giststate->penaltyFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(orig),
PointerGetDatum(add),
- PointerGetDatum(&penalty));
+ PointerGetDatum(&penalty),
+ PointerGetDatum(giststate->opclassoptions[attno]));
/* disallow negative or NaN penalty */
if (isnan(penalty) || penalty < 0.0)
penalty = 0.0;
@@ -860,6 +867,14 @@ gistoptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+bytea *
+gistopclassoptions(Relation index, AttrNumber attnum, Datum options, bool validate)
+{
+ return index_opclass_options_generic(index, attnum, GIST_OPCLASSOPT_PROC,
+ options, validate);
+}
+
+
/*
* gistproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index c300e52..c5e1b23 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -108,37 +108,46 @@ gistvalidate(Oid opclassoid)
{
case GIST_CONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
- 5, 5, INTERNALOID, opcintype,
- INT2OID, OIDOID, INTERNALOID);
+ 5, 6, INTERNALOID, opcintype,
+ INT2OID, OIDOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_UNION_PROC:
ok = check_amproc_signature(procform->amproc, opckeytype, false,
- 2, 2, INTERNALOID, INTERNALOID);
+ 2, 3, INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_COMPRESS_PROC:
case GIST_DECOMPRESS_PROC:
case GIST_FETCH_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 1, 1, INTERNALOID);
+ 1, 2, INTERNALOID, INTERNALOID);
break;
case GIST_PENALTY_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 3, 3, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ 3, 4, INTERNALOID,
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_PICKSPLIT_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 2, 2, INTERNALOID, INTERNALOID);
+ 2, 3, INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_EQUAL_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 3, 3, opckeytype, opckeytype,
- INTERNALOID);
+ 3, 4, opckeytype, opckeytype,
+ INTERNALOID, INTERNALOID);
break;
case GIST_DISTANCE_PROC:
ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
- 5, 5, INTERNALOID, opcintype,
- INT2OID, OIDOID, INTERNALOID);
+ 5, 6, INTERNALOID, opcintype,
+ INT2OID, OIDOID, INTERNALOID,
+ INTERNALOID);
+ break;
+ case GIST_OPCLASSOPT_PROC:
+ ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
+ 2, 2, INTERNALOID, BOOLOID);
break;
default:
ereport(INFO,
@@ -259,7 +268,8 @@ gistvalidate(Oid opclassoid)
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
- i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
+ i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
+ i == GIST_OPCLASSOPT_PROC)
continue; /* optional methods */
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 827566d..08e7ada 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -34,7 +34,8 @@
#define GIST_EQUAL_PROC 7
#define GIST_DISTANCE_PROC 8
#define GIST_FETCH_PROC 9
-#define GISTNProcs 9
+#define GIST_OPCLASSOPT_PROC 10
+#define GISTNProcs 10
/*
* Page opaque data in a GiST index page.
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 36ed724..eedd896 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -94,6 +94,8 @@ typedef struct GISTSTATE
/* Collations to pass to the support functions */
Oid supportCollation[INDEX_MAX_KEYS];
+
+ bytea **opclassoptions; /* parsed opclass-specific options */
} GISTSTATE;
@@ -436,6 +438,8 @@ extern bool gistvalidate(Oid opclassoid);
#define GIST_DEFAULT_FILLFACTOR 90
extern bytea *gistoptions(Datum reloptions, bool validate);
+extern bytea *gistopclassoptions(Relation index, AttrNumber colno,
+ Datum options, bool validate);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
--
2.7.4
[text/x-patch] 0003-Add-opclass-parameters-to-GIN-v02.patch (15.1K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/4-0003-Add-opclass-parameters-to-GIN-v02.patch)
download | inline diff:
From 8a015b2803852a03da2d1bde42951473cca1d3c7 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 16 Jan 2018 01:50:09 +0300
Subject: [PATCH 3/9] Add opclass parameters to GIN
---
doc/src/sgml/xindex.sgml | 7 +++++++
src/backend/access/gin/ginget.c | 11 ++++++-----
src/backend/access/gin/ginlogic.c | 15 +++++++++------
src/backend/access/gin/ginscan.c | 6 ++++--
src/backend/access/gin/ginutil.c | 27 +++++++++++++++++++++------
src/backend/access/gin/ginvalidate.c | 32 ++++++++++++++++++++------------
src/backend/utils/adt/selfuncs.c | 6 ++++--
src/include/access/gin.h | 3 ++-
src/include/access/gin_private.h | 5 +++++
9 files changed, 78 insertions(+), 34 deletions(-)
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 8c5b528..658ec9b 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -665,6 +665,13 @@
</entry>
<entry>6</entry>
</row>
+ <row>
+ <entry><function>options</function></entry>
+ <entry>
+ parse opclass-specific options (optional)
+ </entry>
+ <entry>7</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 8466d94..5eb47e0 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -188,13 +188,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
* case cmp < 0 => not match and continue scan
*----------
*/
- cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
+ cmp = DatumGetInt32(FunctionCall5Coll(&btree->ginstate->comparePartialFn[attnum - 1],
btree->ginstate->supportCollation[attnum - 1],
scanEntry->queryKey,
idatum,
UInt16GetDatum(scanEntry->strategy),
- PointerGetDatum(scanEntry->extra_data)));
-
+ PointerGetDatum(scanEntry->extra_data),
+ PointerGetDatum(btree->ginstate->opclassOptions[attnum - 1])));
if (cmp > 0)
return true;
else if (cmp < 0)
@@ -1508,12 +1508,13 @@ matchPartialInPendingList(GinState *ginstate, Page page,
* case cmp < 0 => not match and continue scan
*----------
*/
- cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
+ cmp = DatumGetInt32(FunctionCall5Coll(&ginstate->comparePartialFn[entry->attnum - 1],
ginstate->supportCollation[entry->attnum - 1],
entry->queryKey,
datum[off - 1],
UInt16GetDatum(entry->strategy),
- PointerGetDatum(entry->extra_data)));
+ PointerGetDatum(entry->extra_data),
+ PointerGetDatum(ginstate->opclassOptions[entry->attnum - 1])));
if (cmp == 0)
return true;
else if (cmp > 0)
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index 2c42d1a..5ec3931 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -76,7 +76,7 @@ directBoolConsistentFn(GinScanKey key)
*/
key->recheckCurItem = true;
- return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
+ return DatumGetBool(FunctionCall9Coll(key->consistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
UInt16GetDatum(key->strategy),
@@ -85,7 +85,8 @@ directBoolConsistentFn(GinScanKey key)
PointerGetDatum(key->extra_data),
PointerGetDatum(&key->recheckCurItem),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
}
/*
@@ -94,7 +95,7 @@ directBoolConsistentFn(GinScanKey key)
static GinTernaryValue
directTriConsistentFn(GinScanKey key)
{
- return DatumGetGinTernaryValue(FunctionCall7Coll(
+ return DatumGetGinTernaryValue(FunctionCall8Coll(
key->triConsistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
@@ -103,7 +104,8 @@ directTriConsistentFn(GinScanKey key)
UInt32GetDatum(key->nuserentries),
PointerGetDatum(key->extra_data),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
}
/*
@@ -116,7 +118,7 @@ shimBoolConsistentFn(GinScanKey key)
{
GinTernaryValue result;
- result = DatumGetGinTernaryValue(FunctionCall7Coll(
+ result = DatumGetGinTernaryValue(FunctionCall8Coll(
key->triConsistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
@@ -125,7 +127,8 @@ shimBoolConsistentFn(GinScanKey key)
UInt32GetDatum(key->nuserentries),
PointerGetDatum(key->extra_data),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
if (result == GIN_MAYBE)
{
key->recheckCurItem = true;
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 8ade431..6594a29 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -156,6 +156,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
key->strategy = strategy;
key->searchMode = searchMode;
key->attnum = attnum;
+ key->opclassOptions = ginstate->opclassOptions[attnum - 1];
ItemPointerSetMin(&key->curItem);
key->curItemMatches = false;
@@ -310,7 +311,7 @@ ginNewScanKey(IndexScanDesc scan)
/* OK to call the extractQueryFn */
queryValues = (Datum *)
- DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
+ DatumGetPointer(FunctionCall8Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
so->ginstate.supportCollation[skey->sk_attno - 1],
skey->sk_argument,
PointerGetDatum(&nQueryValues),
@@ -318,7 +319,8 @@ ginNewScanKey(IndexScanDesc scan)
PointerGetDatum(&partial_matches),
PointerGetDatum(&extra_data),
PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode)));
+ PointerGetDatum(&searchMode),
+ PointerGetDatum(so->ginstate.opclassOptions[skey->sk_attno - 1])));
/*
* If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 0a32182..817bbce 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->amopclassoptions = ginopclassoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -95,6 +96,7 @@ initGinState(GinState *state, Relation index)
state->index = index;
state->oneCol = (origTupdesc->natts == 1) ? true : false;
state->origTupdesc = origTupdesc;
+ state->opclassOptions = RelationGetParsedOpclassOptions(index);
for (i = 0; i < origTupdesc->natts; i++)
{
@@ -403,9 +405,10 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
return 0;
/* both not null, so safe to call the compareFn */
- return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
+ return DatumGetInt32(FunctionCall3Coll(&ginstate->compareFn[attnum - 1],
ginstate->supportCollation[attnum - 1],
- a, b));
+ a, b,
+ PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
}
/*
@@ -441,6 +444,7 @@ typedef struct
{
FmgrInfo *cmpDatumFunc;
Oid collation;
+ Datum options;
bool haveDups;
} cmpEntriesArg;
@@ -462,9 +466,10 @@ cmpEntries(const void *a, const void *b, void *arg)
else if (bb->isnull)
res = -1; /* not-NULL "<" NULL */
else
- res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
+ res = DatumGetInt32(FunctionCall3Coll(data->cmpDatumFunc,
data->collation,
- aa->datum, bb->datum));
+ aa->datum, bb->datum,
+ data->options));
/*
* Detect if we have any duplicates. If there are equal keys, qsort must
@@ -510,11 +515,12 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
/* OK, call the opclass's extractValueFn */
nullFlags = NULL; /* in case extractValue doesn't set it */
entries = (Datum *)
- DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
+ DatumGetPointer(FunctionCall4Coll(&ginstate->extractValueFn[attnum - 1],
ginstate->supportCollation[attnum - 1],
value,
PointerGetDatum(nentries),
- PointerGetDatum(&nullFlags)));
+ PointerGetDatum(&nullFlags),
+ PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
/*
* Generate a placeholder if the item contained no keys.
@@ -557,6 +563,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
arg.collation = ginstate->supportCollation[attnum - 1];
+ arg.options = PointerGetDatum(ginstate->opclassOptions[attnum - 1]);
arg.haveDups = false;
qsort_arg(keydata, *nentries, sizeof(keyEntryData),
cmpEntries, (void *) &arg);
@@ -632,6 +639,14 @@ ginoptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+bytea *
+ginopclassoptions(Relation index, AttrNumber colno, Datum indoptions,
+ bool validate)
+{
+ return index_opclass_options_generic(index, colno, GIN_OPCLASSOPTIONS_PROC,
+ indoptions, validate);
+}
+
/*
* Fetch index's statistical data into *stats
*
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 1922260..4f87d12 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -108,40 +108,47 @@ ginvalidate(Oid opclassoid)
{
case GIN_COMPARE_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
- 2, 2, opckeytype, opckeytype);
+ 2, 3, opckeytype, opckeytype,
+ INTERNALOID);
break;
case GIN_EXTRACTVALUE_PROC:
/* Some opclasses omit nullFlags */
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 2, 3, opcintype, INTERNALOID,
- INTERNALOID);
+ 2, 4, opcintype, INTERNALOID,
+ INTERNALOID, INTERNALOID);
break;
case GIN_EXTRACTQUERY_PROC:
/* Some opclasses omit nullFlags and searchMode */
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 5, 7, opcintype, INTERNALOID,
+ 5, 8, opcintype, INTERNALOID,
INT2OID, INTERNALOID, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIN_CONSISTENT_PROC:
/* Some opclasses omit queryKeys and nullFlags */
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
- 6, 8, INTERNALOID, INT2OID,
+ 6, 9, INTERNALOID, INT2OID,
opcintype, INT4OID,
INTERNALOID, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIN_COMPARE_PARTIAL_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
- 4, 4, opckeytype, opckeytype,
- INT2OID, INTERNALOID);
+ 4, 5, opckeytype, opckeytype,
+ INT2OID, INTERNALOID, INTERNALOID);
break;
case GIN_TRICONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, CHAROID, false,
- 7, 7, INTERNALOID, INT2OID,
+ 7, 8, INTERNALOID, INT2OID,
opcintype, INT4OID,
INTERNALOID, INTERNALOID,
- INTERNALOID);
+ INTERNALOID, INTERNALOID);
+ break;
+ case GIN_OPCLASSOPTIONS_PROC:
+ ok = check_amproc_signature(procform->amproc, INTERNALOID,
+ false, 2, 2, INTERNALOID, BOOLOID);
break;
default:
ereport(INFO,
@@ -238,7 +245,8 @@ ginvalidate(Oid opclassoid)
if (opclassgroup &&
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
- if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC)
+ if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+ i == GIN_OPCLASSOPTIONS_PROC)
continue; /* optional method */
if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
continue; /* don't need both, see check below loop */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index e0ece74..a53f416 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -139,6 +139,7 @@
#include "utils/lsyscache.h"
#include "utils/pg_locale.h"
#include "utils/rel.h"
+#include "utils/relcache.h"
#include "utils/selfuncs.h"
#include "utils/snapmgr.h"
#include "utils/spccache.h"
@@ -7456,7 +7457,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
else
collation = DEFAULT_COLLATION_OID;
- OidFunctionCall7Coll(extractProcOid,
+ OidFunctionCall8Coll(extractProcOid,
collation,
query,
PointerGetDatum(&nentries),
@@ -7464,7 +7465,8 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
PointerGetDatum(&partial_matches),
PointerGetDatum(&extra_data),
PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode));
+ PointerGetDatum(&searchMode),
+ PointerGetDatum(index->opclassoptions[indexcol]));
if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
{
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 3d8a130..20ce792 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -25,7 +25,8 @@
#define GIN_CONSISTENT_PROC 4
#define GIN_COMPARE_PARTIAL_PROC 5
#define GIN_TRICONSISTENT_PROC 6
-#define GINNProcs 6
+#define GIN_OPCLASSOPTIONS_PROC 7
+#define GINNProcs 7
/*
* searchMode settings for extractQueryFn.
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 81bf873..6820899 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -67,6 +67,8 @@ typedef struct GinState
TupleDesc origTupdesc;
TupleDesc tupdesc[INDEX_MAX_KEYS];
+ bytea **opclassOptions; /* per-index-column opclass options */
+
/*
* Per-index-column opclass support functions
*/
@@ -85,6 +87,8 @@ typedef struct GinState
/* ginutil.c */
extern bytea *ginoptions(Datum reloptions, bool validate);
+extern bytea *ginopclassoptions(Relation index, AttrNumber colno,
+ Datum indoptions, bool validate);
extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
@@ -296,6 +300,7 @@ typedef struct GinScanKeyData
StrategyNumber strategy;
int32 searchMode;
OffsetNumber attnum;
+ bytea *opclassOptions;
/*
* Match status data. curItem is the TID most recently tested (could be a
--
2.7.4
[text/x-patch] 0004-Add-opclass-parameters-to-GiST-tsvector_ops-v02.patch (31.8K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/5-0004-Add-opclass-parameters-to-GiST-tsvector_ops-v02.patch)
download | inline diff:
From af502308192d3f316e493682a36f63733881043b Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 16 Jan 2018 18:31:23 +0300
Subject: [PATCH 4/9] Add opclass parameters to GiST tsvector_ops
---
doc/src/sgml/textsearch.sgml | 9 +-
src/backend/utils/adt/tsgistidx.c | 269 +++++++++++++++++++---------------
src/include/catalog/pg_amproc.dat | 5 +-
src/include/catalog/pg_proc.dat | 19 ++-
src/test/regress/expected/tsearch.out | 176 ++++++++++++++++++++++
src/test/regress/sql/tsearch.sql | 45 ++++++
6 files changed, 392 insertions(+), 131 deletions(-)
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index ecebade..92abf6e 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3635,7 +3635,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>
@@ -3643,6 +3643,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>
@@ -3666,7 +3668,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 2d9ecc4..462791a 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -15,23 +15,29 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.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 )
@@ -39,8 +45,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))
@@ -64,9 +70,10 @@ 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) )
@@ -90,7 +97,7 @@ static const uint8 number_of_ones[256] = {
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
Datum
gtsvectorin(PG_FUNCTION_ARGS)
@@ -121,9 +128,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);
@@ -167,36 +175,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_GETARG_POINTER(1))->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--)
@@ -227,13 +248,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;
}
@@ -245,22 +262,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,
@@ -334,12 +346,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
@@ -366,7 +380,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));
}
@@ -384,7 +398,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
{
int32 i;
@@ -395,7 +409,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ Assert(GETSIGLEN(add) == siglen);
+
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
@@ -403,7 +419,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;
}
@@ -414,30 +430,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_GETARG_POINTER(2))->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);
}
@@ -448,6 +458,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_GETARG_POINTER(3))->siglen;
if (ISSIGNKEY(a))
{ /* then b also ISSIGNKEY */
@@ -463,8 +474,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])
{
@@ -501,24 +514,24 @@ gtsvector_same(PG_FUNCTION_ARGS)
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
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]);
dist += number_of_ones[diff];
@@ -529,17 +542,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);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ Assert(siglena == siglenb);
+
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
}
Datum
@@ -548,6 +566,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_GETARG_POINTER(3))->siglen;
SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
BITVECP orig = GETSIGN(origval);
@@ -556,14 +575,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);
@@ -573,19 +600,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) )
@@ -609,19 +636,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
@@ -629,6 +656,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_GETARG_POINTER(2))->siglen;
OffsetNumber k,
j;
SignTSVector *datum_l,
@@ -648,6 +676,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
BITVECP ptr;
int i;
CACHESIGN *cache;
+ char *cache_sign;
SPLITCOST *costvector;
maxoff = entryvec->n - 2;
@@ -656,16 +685,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;
@@ -687,44 +722,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);
@@ -750,36 +762,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;
@@ -790,12 +800,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;
@@ -822,3 +832,20 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
{
return gtsvector_consistent(fcinfo);
}
+
+Datum
+gtsvector_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(GistTsVectorOptions, siglen) };
+ GistTsVectorOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(GistTsVectorOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 0ef2c08..c1ee3db 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -450,7 +450,7 @@
amproc => 'gist_circle_distance' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '1',
- amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal)' },
+ amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal,internal)' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '2',
amproc => 'gtsvector_union' },
@@ -468,6 +468,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 => '10',
+ 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 9264a2e..c22d639 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8430,30 +8430,35 @@
{ oid => '3648', descr => 'GiST tsvector support',
proname => 'gtsvector_compress', prorettype => 'internal',
- proargtypes => 'internal', prosrc => 'gtsvector_compress' },
+ proargtypes => 'internal internal', prosrc => 'gtsvector_compress' },
{ oid => '3649', descr => 'GiST tsvector support',
proname => 'gtsvector_decompress', prorettype => 'internal',
- proargtypes => 'internal', prosrc => 'gtsvector_decompress' },
+ proargtypes => 'internal internal', prosrc => 'gtsvector_decompress' },
{ oid => '3650', descr => 'GiST tsvector support',
proname => 'gtsvector_picksplit', prorettype => 'internal',
- proargtypes => 'internal internal', prosrc => 'gtsvector_picksplit' },
+ proargtypes => 'internal internal internal', prosrc => 'gtsvector_picksplit' },
{ oid => '3651', descr => 'GiST tsvector support',
proname => 'gtsvector_union', prorettype => 'gtsvector',
- proargtypes => 'internal internal', prosrc => 'gtsvector_union' },
+ proargtypes => 'internal internal internal', prosrc => 'gtsvector_union' },
{ oid => '3652', descr => 'GiST tsvector support',
proname => 'gtsvector_same', prorettype => 'internal',
- proargtypes => 'gtsvector gtsvector internal', prosrc => 'gtsvector_same' },
+ proargtypes => 'gtsvector gtsvector internal internal',
+ prosrc => 'gtsvector_same' },
{ oid => '3653', descr => 'GiST tsvector support',
proname => 'gtsvector_penalty', prorettype => 'internal',
- proargtypes => 'internal internal internal', prosrc => 'gtsvector_penalty' },
+ proargtypes => 'internal internal internal internal',
+ prosrc => 'gtsvector_penalty' },
{ oid => '3654', descr => 'GiST tsvector support',
proname => 'gtsvector_consistent', prorettype => 'bool',
- proargtypes => 'internal tsvector int2 oid internal',
+ proargtypes => 'internal tsvector int2 oid internal internal',
prosrc => 'gtsvector_consistent' },
{ oid => '3790', descr => 'GiST tsvector support (obsolete)',
proname => 'gtsvector_consistent', prorettype => 'bool',
proargtypes => 'internal gtsvector int4 oid internal',
prosrc => 'gtsvector_consistent_oldsig' },
+{ oid => '3996', descr => 'GiST tsvector support',
+ proname => 'gtsvector_options', prorettype => 'internal',
+ proargtypes => 'internal bool', 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 b088ff0..9d81489 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 637bfb3..740d5e0 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-Add-opclass-parameters-to-contrib-intarray-v02.patch (55.2K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/6-0005-Add-opclass-parameters-to-contrib-intarray-v02.patch)
download | inline diff:
From c246a65e9a613f57e8812d32c395705bb940cfef Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 15 Jan 2018 17:09:22 +0300
Subject: [PATCH 5/9] Add opclass parameters to contrib/intarray
---
contrib/intarray/Makefile | 4 +-
contrib/intarray/_int.h | 38 ++-
contrib/intarray/_int_bool.c | 34 +--
contrib/intarray/_int_gist.c | 31 +-
contrib/intarray/_int_tool.c | 4 +-
contrib/intarray/_intbig_gist.c | 200 +++++++------
contrib/intarray/intarray--1.2-1.3.sql | 34 +++
contrib/intarray/intarray--1.2.sql | 520 --------------------------------
contrib/intarray/intarray--1.3.sql | 531 +++++++++++++++++++++++++++++++++
contrib/intarray/intarray.control | 2 +-
doc/src/sgml/intarray.sgml | 14 +-
11 files changed, 758 insertions(+), 654 deletions(-)
create mode 100644 contrib/intarray/intarray--1.2-1.3.sql
delete mode 100644 contrib/intarray/intarray--1.2.sql
create mode 100644 contrib/intarray/intarray--1.3.sql
diff --git a/contrib/intarray/Makefile b/contrib/intarray/Makefile
index 2505294..d99d977 100644
--- a/contrib/intarray/Makefile
+++ b/contrib/intarray/Makefile
@@ -5,8 +5,8 @@ OBJS = _int_bool.o _int_gist.o _int_op.o _int_tool.o \
_intbig_gist.o _int_gin.o _int_selfuncs.o $(WIN32RES)
EXTENSION = intarray
-DATA = intarray--1.2.sql intarray--1.1--1.2.sql intarray--1.0--1.1.sql \
- intarray--unpackaged--1.0.sql
+DATA = intarray--1.3.sql intarray--1.2-1.3.sql intarray--1.1--1.2.sql \
+ intarray--1.0--1.1.sql intarray--unpackaged--1.0.sql
PGFILEDESC = "intarray - functions and operators for arrays of integers"
REGRESS = _int
diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index b689eb7..87d21e9 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -8,7 +8,15 @@
#include "utils/memutils.h"
/* number ranges for compression */
-#define MAXNUMRANGE 100
+#define G_INT_NUMRANGES_DEFAULT 100
+#define G_INT_NUMRANGES_MAX 1000
+
+/* gist_int_ops opclass options */
+typedef struct IntArrayOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int num_ranges; /* number of ranges */
+} IntArrayOptions;
/* useful macros for accessing int4 arrays */
#define ARRPTR(x) ( (int32 *) ARR_DATA_PTR(x) )
@@ -47,15 +55,14 @@
/* bigint defines */
-#define SIGLENINT 63 /* >122 => key will toast, so very slow!!! */
-#define SIGLEN ( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITS_PER_BYTE)
+#define SIGLEN_DEFAULT (63 * 4)
+#define SIGLEN_MAX (122 * 4) /* key will toast, so very slow!!! */
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < siglen; i++)
/* beware of multiple evaluation of arguments to these macros! */
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
@@ -63,8 +70,15 @@ typedef char *BITVECP;
#define CLRBIT(x,i) GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITS_PER_BYTE ) )
#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))
+
+/* gist_intbig_ops opclass options */
+typedef struct IntArrayBigOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} IntArrayBigOptions;
/*
* type of index key
@@ -81,7 +95,7 @@ typedef struct
#define ISALLTRUE(x) ( ((GISTTYPE*)x)->flag & ALLISTRUE )
#define GTHDRSIZE (VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
#define GETSIGN(x) ( (BITVECP)( (char*)x+GTHDRSIZE ) )
@@ -109,7 +123,7 @@ bool inner_int_contains(ArrayType *a, ArrayType *b);
ArrayType *inner_int_union(ArrayType *a, ArrayType *b);
ArrayType *inner_int_inter(ArrayType *a, ArrayType *b);
void rt__int_size(ArrayType *a, float *size);
-void gensign(BITVEC sign, int *a, int len);
+void gensign(BITVECP sign, int *a, int len, int siglen);
/*****************************************************************************
@@ -155,7 +169,7 @@ typedef struct QUERYTYPE
#define PG_GETARG_QUERYTYPE_P(n) DatumGetQueryTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_QUERYTYPE_P_COPY(n) DatumGetQueryTypePCopy(PG_GETARG_DATUM(n))
-bool signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot);
+bool signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot);
bool execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
bool gin_bool_consistent(QUERYTYPE *query, bool *check);
diff --git a/contrib/intarray/_int_bool.c b/contrib/intarray/_int_bool.c
index 91e2a80..4e7d55a 100644
--- a/contrib/intarray/_int_bool.c
+++ b/contrib/intarray/_int_bool.c
@@ -233,7 +233,7 @@ typedef struct
* is there value 'val' in (sorted) array or not ?
*/
static bool
-checkcondition_arr(void *checkval, ITEM *item)
+checkcondition_arr(void *checkval, ITEM *item, void *options)
{
int32 *StopLow = ((CHKVAL *) checkval)->arrb;
int32 *StopHigh = ((CHKVAL *) checkval)->arre;
@@ -255,42 +255,42 @@ checkcondition_arr(void *checkval, ITEM *item)
}
static bool
-checkcondition_bit(void *checkval, ITEM *item)
+checkcondition_bit(void *checkval, ITEM *item, void *siglen)
{
- return GETBIT(checkval, HASHVAL(item->val));
+ return GETBIT(checkval, HASHVAL(item->val, (int)(intptr_t) siglen));
}
/*
* evaluate boolean expression, using chkcond() to test the primitive cases
*/
static bool
-execute(ITEM *curitem, void *checkval, bool calcnot,
- bool (*chkcond) (void *checkval, ITEM *item))
+execute(ITEM *curitem, void *checkval, void *options, bool calcnot,
+ bool (*chkcond) (void *checkval, ITEM *item, void *options))
{
/* since this function recurses, it could be driven to stack overflow */
check_stack_depth();
if (curitem->type == VAL)
- return (*chkcond) (checkval, curitem);
+ return (*chkcond) (checkval, curitem, options);
else if (curitem->val == (int32) '!')
{
return calcnot ?
- ((execute(curitem - 1, checkval, calcnot, chkcond)) ? false : true)
+ ((execute(curitem - 1, checkval, options, calcnot, chkcond)) ? false : true)
: true;
}
else if (curitem->val == (int32) '&')
{
- if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
- return execute(curitem - 1, checkval, calcnot, chkcond);
+ if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
+ return execute(curitem - 1, checkval, options, calcnot, chkcond);
else
return false;
}
else
{ /* |-operator */
- if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
+ if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
return true;
else
- return execute(curitem - 1, checkval, calcnot, chkcond);
+ return execute(curitem - 1, checkval, options, calcnot, chkcond);
}
}
@@ -298,10 +298,10 @@ execute(ITEM *curitem, void *checkval, bool calcnot,
* signconsistent & execconsistent called by *_consistent
*/
bool
-signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot)
+signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot)
{
return execute(GETQUERY(query) + query->size - 1,
- (void *) sign, calcnot,
+ (void *) sign, (void *)(intptr_t) siglen, calcnot,
checkcondition_bit);
}
@@ -315,7 +315,7 @@ execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot)
chkval.arrb = ARRPTR(array);
chkval.arre = chkval.arrb + ARRNELEMS(array);
return execute(GETQUERY(query) + query->size - 1,
- (void *) &chkval, calcnot,
+ (void *) &chkval, NULL, calcnot,
checkcondition_arr);
}
@@ -326,7 +326,7 @@ typedef struct
} GinChkVal;
static bool
-checkcondition_gin(void *checkval, ITEM *item)
+checkcondition_gin(void *checkval, ITEM *item, void *options)
{
GinChkVal *gcv = (GinChkVal *) checkval;
@@ -357,7 +357,7 @@ gin_bool_consistent(QUERYTYPE *query, bool *check)
}
return execute(GETQUERY(query) + query->size - 1,
- (void *) &gcv, true,
+ (void *) &gcv, NULL, true,
checkcondition_gin);
}
@@ -429,7 +429,7 @@ boolop(PG_FUNCTION_ARGS)
chkval.arrb = ARRPTR(val);
chkval.arre = chkval.arrb + ARRNELEMS(val);
result = execute(GETQUERY(query) + query->size - 1,
- &chkval, true,
+ &chkval, NULL, true,
checkcondition_arr);
pfree(val);
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index 911d180..9de7f55 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -6,6 +6,7 @@
#include <limits.h>
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "_int.h"
@@ -22,6 +23,7 @@ PG_FUNCTION_INFO_V1(g_int_penalty);
PG_FUNCTION_INFO_V1(g_int_picksplit);
PG_FUNCTION_INFO_V1(g_int_union);
PG_FUNCTION_INFO_V1(g_int_same);
+PG_FUNCTION_INFO_V1(g_int_options);
/*
@@ -139,6 +141,7 @@ Datum
g_int_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ IntArrayOptions *opts = (IntArrayOptions *) PG_GETARG_POINTER(1);
GISTENTRY *retval;
ArrayType *r;
int len;
@@ -153,9 +156,9 @@ g_int_compress(PG_FUNCTION_ARGS)
CHECKARRVALID(r);
PREPAREARR(r);
- if (ARRNELEMS(r) >= 2 * MAXNUMRANGE)
+ if (ARRNELEMS(r) >= 2 * opts->num_ranges)
elog(NOTICE, "input array is too big (%d maximum allowed, %d current), use gist__intbig_ops opclass instead",
- 2 * MAXNUMRANGE - 1, ARRNELEMS(r));
+ 2 * opts->num_ranges - 1, ARRNELEMS(r));
retval = palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(r),
@@ -178,7 +181,7 @@ g_int_compress(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(entry);
}
- if ((len = ARRNELEMS(r)) >= 2 * MAXNUMRANGE)
+ if ((len = ARRNELEMS(r)) >= 2 * opts->num_ranges)
{ /* compress */
if (r == (ArrayType *) DatumGetPointer(entry->key))
r = DatumGetArrayTypePCopy(entry->key);
@@ -191,7 +194,7 @@ g_int_compress(PG_FUNCTION_ARGS)
len *= 2;
cand = 1;
- while (len > MAXNUMRANGE * 2)
+ while (len > opts->num_ranges * 2)
{
min = INT_MAX;
for (i = 2; i < len; i += 2)
@@ -217,6 +220,7 @@ Datum
g_int_decompress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ IntArrayOptions *opts = (IntArrayOptions *) PG_GETARG_POINTER(1);
GISTENTRY *retval;
ArrayType *r;
int *dr,
@@ -245,7 +249,7 @@ g_int_decompress(PG_FUNCTION_ARGS)
lenin = ARRNELEMS(in);
- if (lenin < 2 * MAXNUMRANGE)
+ if (lenin < 2 * opts->num_ranges)
{ /* not compressed value */
if (in != (ArrayType *) DatumGetPointer(entry->key))
{
@@ -542,3 +546,20 @@ g_int_picksplit(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(v);
}
+
+Datum
+g_int_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"numranges", "number of ranges for compression", 0, 0, 9, RELOPT_TYPE_INT },
+ G_INT_NUMRANGES_DEFAULT, 1, G_INT_NUMRANGES_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(IntArrayOptions, num_ranges) };
+ IntArrayOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(IntArrayOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index d86485d..9bec6b9 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -324,14 +324,14 @@ _int_unique(ArrayType *r)
}
void
-gensign(BITVEC sign, int *a, int len)
+gensign(BITVECP sign, int *a, int len, int siglen)
{
int i;
/* we assume that the sign vector is previously zeroed */
for (i = 0; i < len; i++)
{
- HASH(sign, *a);
+ HASH(sign, *a, siglen);
a++;
}
}
diff --git a/contrib/intarray/_intbig_gist.c b/contrib/intarray/_intbig_gist.c
index de7bc82..4d2b3a2 100644
--- a/contrib/intarray/_intbig_gist.c
+++ b/contrib/intarray/_intbig_gist.c
@@ -4,6 +4,7 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "_int.h"
@@ -19,6 +20,7 @@ PG_FUNCTION_INFO_V1(g_intbig_penalty);
PG_FUNCTION_INFO_V1(g_intbig_picksplit);
PG_FUNCTION_INFO_V1(g_intbig_union);
PG_FUNCTION_INFO_V1(g_intbig_same);
+PG_FUNCTION_INFO_V1(g_intbig_options);
/* Number of one-bits in an unsigned byte */
static const uint8 number_of_ones[256] = {
@@ -61,12 +63,33 @@ _intbig_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+static GISTTYPE *
+_intbig_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+ int flag = allistrue ? ALLISTRUE : 0;
+ int size = CALCGTSIZE(flag, siglen);
+ GISTTYPE *res = (GISTTYPE *) palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if (!allistrue)
+ {
+ if (sign)
+ memcpy(GETSIGN(res), sign, siglen);
+ else
+ memset(GETSIGN(res), 0, siglen);
+ }
+
+ return res;
+}
+
/*********************************************************************
** intbig functions
*********************************************************************/
static bool
-_intbig_overlap(GISTTYPE *a, ArrayType *b)
+_intbig_overlap(GISTTYPE *a, ArrayType *b, int siglen)
{
int num = ARRNELEMS(b);
int32 *ptr = ARRPTR(b);
@@ -75,7 +98,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
while (num--)
{
- if (GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+ if (GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
return true;
ptr++;
}
@@ -84,7 +107,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
}
static bool
-_intbig_contains(GISTTYPE *a, ArrayType *b)
+_intbig_contains(GISTTYPE *a, ArrayType *b, int siglen)
{
int num = ARRNELEMS(b);
int32 *ptr = ARRPTR(b);
@@ -93,7 +116,7 @@ _intbig_contains(GISTTYPE *a, ArrayType *b)
while (num--)
{
- if (!GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+ if (!GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
return false;
ptr++;
}
@@ -107,6 +130,8 @@ g_intbig_same(PG_FUNCTION_ARGS)
GISTTYPE *a = (GISTTYPE *) PG_GETARG_POINTER(0);
GISTTYPE *b = (GISTTYPE *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(3);
+ int siglen = opts->siglen;
if (ISALLTRUE(a) && ISALLTRUE(b))
*result = true;
@@ -121,7 +146,7 @@ g_intbig_same(PG_FUNCTION_ARGS)
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -137,6 +162,8 @@ Datum
g_intbig_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(1);
+ int siglen = opts->siglen;
if (entry->leafkey)
{
@@ -144,7 +171,7 @@ g_intbig_compress(PG_FUNCTION_ARGS)
ArrayType *in = DatumGetArrayTypeP(entry->key);
int32 *ptr;
int num;
- GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+ GISTTYPE *res = _intbig_alloc(false, siglen, NULL);
CHECKARRVALID(in);
if (ARRISEMPTY(in))
@@ -157,11 +184,10 @@ g_intbig_compress(PG_FUNCTION_ARGS)
ptr = ARRPTR(in);
num = ARRNELEMS(in);
}
- SET_VARSIZE(res, CALCGTSIZE(0));
while (num--)
{
- HASH(GETSIGN(res), *ptr);
+ HASH(GETSIGN(res), *ptr, siglen);
ptr++;
}
@@ -182,16 +208,13 @@ g_intbig_compress(PG_FUNCTION_ARGS)
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
GISTTYPE *res;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(entry);
}
- res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
- SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
- res->flag = ALLISTRUE;
-
+ res = _intbig_alloc(true, siglen, sign);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
entry->rel, entry->page,
@@ -205,24 +228,24 @@ g_intbig_compress(PG_FUNCTION_ARGS)
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
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]);
dist += number_of_ones[diff];
@@ -231,19 +254,19 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *b, int siglen)
{
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(b), siglen);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(a), siglen);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglen);
}
Datum
@@ -253,14 +276,14 @@ g_intbig_decompress(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
{
int32 i;
BITVECP sadd = GETSIGN(add);
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
return 0;
}
@@ -270,29 +293,23 @@ g_intbig_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;
- GISTTYPE *result;
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(2);
+ int siglen = opts->siglen;
+ int32 i;
+ GISTTYPE *result = _intbig_alloc(false, siglen, NULL);
+ BITVECP base = GETSIGN(result);
- 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(ALLISTRUE, siglen));
break;
}
}
- len = CALCGTSIZE(flag);
- result = (GISTTYPE *) palloc(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -305,8 +322,10 @@ g_intbig_penalty(PG_FUNCTION_ARGS)
float *penalty = (float *) PG_GETARG_POINTER(2);
GISTTYPE *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
GISTTYPE *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(3);
+ int siglen = opts->siglen;
- *penalty = hemdist(origval, newval);
+ *penalty = hemdist(origval, newval, siglen);
PG_RETURN_POINTER(penalty);
}
@@ -329,6 +348,8 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(2);
+ int siglen = opts->siglen;
OffsetNumber k,
j;
GISTTYPE *datum_l,
@@ -361,7 +382,7 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
_k = GETENTRY(entryvec, k);
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
- size_waste = hemdist(_k, GETENTRY(entryvec, j));
+ size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -383,32 +404,10 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_l, GTHDRSIZE);
- datum_l->flag = ALLISTRUE;
- }
- else
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
- datum_l->flag = 0;
- memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC));
- }
- if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_r, GTHDRSIZE);
- datum_r->flag = ALLISTRUE;
- }
- else
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
- datum_r->flag = 0;
- memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
- }
+ datum_l = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_1)));
+ datum_r = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_2)));
maxoff = OffsetNumberNext(maxoff);
/* sort before ... */
@@ -417,8 +416,8 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
{
costvector[j - 1].pos = j;
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -442,20 +441,20 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
continue;
}
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
{
if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_l, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -466,12 +465,12 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_r))
- MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_r, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -497,6 +496,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(5);
+ int siglen = opts->siglen;
bool retval;
/* All cases served by this function are inexact */
@@ -509,6 +510,7 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
{
retval = signconsistent((QUERYTYPE *) query,
GETSIGN(DatumGetPointer(entry->key)),
+ siglen,
false);
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(retval);
@@ -519,7 +521,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
switch (strategy)
{
case RTOverlapStrategyNumber:
- retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
case RTSameStrategyNumber:
if (GIST_LEAF(entry))
@@ -527,22 +530,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
int i,
num = ARRNELEMS(query);
int32 *ptr = ARRPTR(query);
- BITVEC qp;
- BITVECP dq,
+ BITVECP dq = palloc0(siglen),
de;
- memset(qp, 0, sizeof(BITVEC));
-
while (num--)
{
- HASH(qp, *ptr);
+ HASH(dq, *ptr, siglen);
ptr++;
}
de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
- dq = qp;
retval = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (de[i] != dq[i])
{
@@ -551,13 +550,16 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
}
}
+ pfree(dq);
}
else
- retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
case RTContainsStrategyNumber:
case RTOldContainsStrategyNumber:
- retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
case RTContainedByStrategyNumber:
case RTOldContainedByStrategyNumber:
@@ -566,22 +568,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
int i,
num = ARRNELEMS(query);
int32 *ptr = ARRPTR(query);
- BITVEC qp;
- BITVECP dq,
+ BITVECP dq = palloc0(siglen),
de;
- memset(qp, 0, sizeof(BITVEC));
-
while (num--)
{
- HASH(qp, *ptr);
+ HASH(dq, *ptr, siglen);
ptr++;
}
de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
- dq = qp;
retval = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (de[i] & ~dq[i])
{
@@ -591,7 +589,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
}
}
else
- retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
default:
retval = false;
@@ -599,3 +598,20 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(retval);
}
+
+Datum
+g_intbig_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, NoLock, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(IntArrayBigOptions, siglen) };
+ IntArrayBigOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(IntArrayBigOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/contrib/intarray/intarray--1.2-1.3.sql b/contrib/intarray/intarray--1.2-1.3.sql
new file mode 100644
index 0000000..c9b83ef
--- /dev/null
+++ b/contrib/intarray/intarray--1.2-1.3.sql
@@ -0,0 +1,34 @@
+/* contrib/intarray/intarray--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION intarray UPDATE TO '1.3'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('g_int_compress(internal)', '{internal,internal}'),
+('g_int_decompress(internal)', '{internal,internal}'),
+('g_intbig_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}'),
+('g_intbig_compress(internal)', '{internal,internal}'),
+('g_intbig_decompress(internal)', '{internal,internal}'),
+('g_intbig_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('g_intbig_picksplit(internal,internal)', '{internal,internal,internal}'),
+('g_intbig_union(internal,internal)', '{internal,internal,internal}'),
+('g_intbig_same(intbig_gkey,intbig_gkey,internal)', '{intbig_gkey,intbig_gkey,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION g_intbig_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_intbig_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist__intbig_ops USING gist
+ADD FUNCTION 10 (_int4) g_intbig_options (internal, boolean);
diff --git a/contrib/intarray/intarray--1.2.sql b/contrib/intarray/intarray--1.2.sql
deleted file mode 100644
index f10b53d..0000000
--- a/contrib/intarray/intarray--1.2.sql
+++ /dev/null
@@ -1,520 +0,0 @@
-/* contrib/intarray/intarray--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION intarray" to load this file. \quit
-
---
--- Create the user-defined type for the 1-D integer arrays (_int4)
---
-
--- Query type
-CREATE FUNCTION bqarr_in(cstring)
-RETURNS query_int
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION bqarr_out(query_int)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE TYPE query_int (
- INTERNALLENGTH = -1,
- INPUT = bqarr_in,
- OUTPUT = bqarr_out
-);
-
---only for debug
-CREATE FUNCTION querytree(query_int)
-RETURNS text
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-
-CREATE FUNCTION boolop(_int4, query_int)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION boolop(_int4, query_int) IS 'boolean operation with array';
-
-CREATE FUNCTION rboolop(query_int, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION rboolop(query_int, _int4) IS 'boolean operation with array';
-
-CREATE FUNCTION _int_matchsel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE OPERATOR @@ (
- LEFTARG = _int4,
- RIGHTARG = query_int,
- PROCEDURE = boolop,
- COMMUTATOR = '~~',
- RESTRICT = _int_matchsel,
- JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~~ (
- LEFTARG = query_int,
- RIGHTARG = _int4,
- PROCEDURE = rboolop,
- COMMUTATOR = '@@',
- RESTRICT = _int_matchsel,
- JOIN = contjoinsel
-);
-
-
---
--- External C-functions for R-tree methods
---
-
--- Comparison methods
-
-CREATE FUNCTION _int_contains(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_contains(_int4, _int4) IS 'contains';
-
-CREATE FUNCTION _int_contained(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_contained(_int4, _int4) IS 'contained in';
-
-CREATE FUNCTION _int_overlap(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_overlap(_int4, _int4) IS 'overlaps';
-
-CREATE FUNCTION _int_same(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_same(_int4, _int4) IS 'same as';
-
-CREATE FUNCTION _int_different(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_different(_int4, _int4) IS 'different';
-
--- support routines for indexing
-
-CREATE FUNCTION _int_union(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_inter(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_overlap_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contains_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contained_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_overlap_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contains_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contained_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
---
--- OPERATORS
---
-
-CREATE OPERATOR && (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_overlap,
- COMMUTATOR = '&&',
- RESTRICT = _int_overlap_sel,
- JOIN = _int_overlap_joinsel
-);
-
---CREATE OPERATOR = (
--- LEFTARG = _int4,
--- RIGHTARG = _int4,
--- PROCEDURE = _int_same,
--- COMMUTATOR = '=',
--- NEGATOR = '<>',
--- RESTRICT = eqsel,
--- JOIN = eqjoinsel,
--- SORT1 = '<',
--- SORT2 = '<'
---);
-
---CREATE OPERATOR <> (
--- LEFTARG = _int4,
--- RIGHTARG = _int4,
--- PROCEDURE = _int_different,
--- COMMUTATOR = '<>',
--- NEGATOR = '=',
--- RESTRICT = neqsel,
--- JOIN = neqjoinsel
---);
-
-CREATE OPERATOR @> (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contains,
- COMMUTATOR = '<@',
- RESTRICT = _int_contains_sel,
- JOIN = _int_contains_joinsel
-);
-
-CREATE OPERATOR <@ (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contained,
- COMMUTATOR = '@>',
- RESTRICT = _int_contained_sel,
- JOIN = _int_contained_joinsel
-);
-
--- obsolete:
-CREATE OPERATOR @ (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contains,
- COMMUTATOR = '~',
- RESTRICT = _int_contains_sel,
- JOIN = _int_contains_joinsel
-);
-
-CREATE OPERATOR ~ (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contained,
- COMMUTATOR = '@',
- RESTRICT = _int_contained_sel,
- JOIN = _int_contained_joinsel
-);
-
---------------
-CREATE FUNCTION intset(int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION icount(_int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR # (
- RIGHTARG = _int4,
- PROCEDURE = icount
-);
-
-CREATE FUNCTION sort(_int4, text)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort_asc(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort_desc(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION uniq(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION idx(_int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR # (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = idx
-);
-
-CREATE FUNCTION subarray(_int4, int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION subarray(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION intarray_push_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR + (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = intarray_push_elem
-);
-
-CREATE FUNCTION intarray_push_array(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR + (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- COMMUTATOR = +,
- PROCEDURE = intarray_push_array
-);
-
-CREATE FUNCTION intarray_del_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR - (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = intarray_del_elem
-);
-
-CREATE FUNCTION intset_union_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR | (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = intset_union_elem
-);
-
-CREATE OPERATOR | (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- COMMUTATOR = |,
- PROCEDURE = _int_union
-);
-
-CREATE FUNCTION intset_subtract(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR - (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = intset_subtract
-);
-
-CREATE OPERATOR & (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- COMMUTATOR = &,
- PROCEDURE = _int_inter
-);
---------------
-
--- define the GiST support methods
-CREATE FUNCTION g_int_consistent(internal,_int4,smallint,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_union(internal, internal)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_same(_int4, _int4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-
--- Create the operator class for indexing
-
-CREATE OPERATOR CLASS gist__int_ops
-DEFAULT FOR TYPE _int4 USING gist AS
- OPERATOR 3 &&,
- OPERATOR 6 = (anyarray, anyarray),
- OPERATOR 7 @>,
- OPERATOR 8 <@,
- OPERATOR 13 @,
- OPERATOR 14 ~,
- OPERATOR 20 @@ (_int4, query_int),
- FUNCTION 1 g_int_consistent (internal, _int4, smallint, oid, internal),
- FUNCTION 2 g_int_union (internal, internal),
- FUNCTION 3 g_int_compress (internal),
- FUNCTION 4 g_int_decompress (internal),
- FUNCTION 5 g_int_penalty (internal, internal, internal),
- FUNCTION 6 g_int_picksplit (internal, internal),
- FUNCTION 7 g_int_same (_int4, _int4, internal);
-
-
----------------------------------------------
--- intbig
----------------------------------------------
--- define the GiST support methods
-
-CREATE FUNCTION _intbig_in(cstring)
-RETURNS intbig_gkey
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _intbig_out(intbig_gkey)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE TYPE intbig_gkey (
- INTERNALLENGTH = -1,
- INPUT = _intbig_in,
- OUTPUT = _intbig_out
-);
-
-CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_union(internal, internal)
-RETURNS intbig_gkey
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
--- register the opclass for indexing (not as default)
-
-CREATE OPERATOR CLASS gist__intbig_ops
-FOR TYPE _int4 USING gist
-AS
- OPERATOR 3 &&,
- OPERATOR 6 = (anyarray, anyarray),
- OPERATOR 7 @>,
- OPERATOR 8 <@,
- OPERATOR 13 @,
- OPERATOR 14 ~,
- OPERATOR 20 @@ (_int4, query_int),
- FUNCTION 1 g_intbig_consistent (internal, _int4, smallint, oid, internal),
- FUNCTION 2 g_intbig_union (internal, internal),
- FUNCTION 3 g_intbig_compress (internal),
- FUNCTION 4 g_intbig_decompress (internal),
- FUNCTION 5 g_intbig_penalty (internal, internal, internal),
- FUNCTION 6 g_intbig_picksplit (internal, internal),
- FUNCTION 7 g_intbig_same (intbig_gkey, intbig_gkey, internal),
- STORAGE intbig_gkey;
-
---GIN
-
-CREATE FUNCTION ginint4_queryextract(_int4, internal, int2, internal, internal, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION ginint4_consistent(internal, int2, _int4, int4, internal, internal, internal, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE OPERATOR CLASS gin__int_ops
-FOR TYPE _int4 USING gin
-AS
- OPERATOR 3 &&,
- OPERATOR 6 = (anyarray, anyarray),
- OPERATOR 7 @>,
- OPERATOR 8 <@,
- OPERATOR 13 @,
- OPERATOR 14 ~,
- OPERATOR 20 @@ (_int4, query_int),
- FUNCTION 1 btint4cmp (int4, int4),
- FUNCTION 2 ginarrayextract (anyarray, internal, internal),
- FUNCTION 3 ginint4_queryextract (_int4, internal, int2, internal, internal, internal, internal),
- FUNCTION 4 ginint4_consistent (internal, int2, _int4, int4, internal, internal, internal, internal),
- STORAGE int4;
diff --git a/contrib/intarray/intarray--1.3.sql b/contrib/intarray/intarray--1.3.sql
new file mode 100644
index 0000000..6c91c75
--- /dev/null
+++ b/contrib/intarray/intarray--1.3.sql
@@ -0,0 +1,531 @@
+/* contrib/intarray/intarray--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION intarray" to load this file. \quit
+
+--
+-- Create the user-defined type for the 1-D integer arrays (_int4)
+--
+
+-- Query type
+CREATE FUNCTION bqarr_in(cstring)
+RETURNS query_int
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION bqarr_out(query_int)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE query_int (
+ INTERNALLENGTH = -1,
+ INPUT = bqarr_in,
+ OUTPUT = bqarr_out
+);
+
+--only for debug
+CREATE FUNCTION querytree(query_int)
+RETURNS text
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+
+CREATE FUNCTION boolop(_int4, query_int)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION boolop(_int4, query_int) IS 'boolean operation with array';
+
+CREATE FUNCTION rboolop(query_int, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION rboolop(query_int, _int4) IS 'boolean operation with array';
+
+CREATE FUNCTION _int_matchsel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE OPERATOR @@ (
+ LEFTARG = _int4,
+ RIGHTARG = query_int,
+ PROCEDURE = boolop,
+ COMMUTATOR = '~~',
+ RESTRICT = _int_matchsel,
+ JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~~ (
+ LEFTARG = query_int,
+ RIGHTARG = _int4,
+ PROCEDURE = rboolop,
+ COMMUTATOR = '@@',
+ RESTRICT = _int_matchsel,
+ JOIN = contjoinsel
+);
+
+
+--
+-- External C-functions for R-tree methods
+--
+
+-- Comparison methods
+
+CREATE FUNCTION _int_contains(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_contains(_int4, _int4) IS 'contains';
+
+CREATE FUNCTION _int_contained(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_contained(_int4, _int4) IS 'contained in';
+
+CREATE FUNCTION _int_overlap(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_overlap(_int4, _int4) IS 'overlaps';
+
+CREATE FUNCTION _int_same(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_same(_int4, _int4) IS 'same as';
+
+CREATE FUNCTION _int_different(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_different(_int4, _int4) IS 'different';
+
+-- support routines for indexing
+
+CREATE FUNCTION _int_union(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_inter(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_overlap_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contains_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contained_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_overlap_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contains_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contained_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+--
+-- OPERATORS
+--
+
+CREATE OPERATOR && (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_overlap,
+ COMMUTATOR = '&&',
+ RESTRICT = _int_overlap_sel,
+ JOIN = _int_overlap_joinsel
+);
+
+--CREATE OPERATOR = (
+-- LEFTARG = _int4,
+-- RIGHTARG = _int4,
+-- PROCEDURE = _int_same,
+-- COMMUTATOR = '=',
+-- NEGATOR = '<>',
+-- RESTRICT = eqsel,
+-- JOIN = eqjoinsel,
+-- SORT1 = '<',
+-- SORT2 = '<'
+--);
+
+--CREATE OPERATOR <> (
+-- LEFTARG = _int4,
+-- RIGHTARG = _int4,
+-- PROCEDURE = _int_different,
+-- COMMUTATOR = '<>',
+-- NEGATOR = '=',
+-- RESTRICT = neqsel,
+-- JOIN = neqjoinsel
+--);
+
+CREATE OPERATOR @> (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contains,
+ COMMUTATOR = '<@',
+ RESTRICT = _int_contains_sel,
+ JOIN = _int_contains_joinsel
+);
+
+CREATE OPERATOR <@ (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contained,
+ COMMUTATOR = '@>',
+ RESTRICT = _int_contained_sel,
+ JOIN = _int_contained_joinsel
+);
+
+-- obsolete:
+CREATE OPERATOR @ (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contains,
+ COMMUTATOR = '~',
+ RESTRICT = _int_contains_sel,
+ JOIN = _int_contains_joinsel
+);
+
+CREATE OPERATOR ~ (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contained,
+ COMMUTATOR = '@',
+ RESTRICT = _int_contained_sel,
+ JOIN = _int_contained_joinsel
+);
+
+--------------
+CREATE FUNCTION intset(int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION icount(_int4)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR # (
+ RIGHTARG = _int4,
+ PROCEDURE = icount
+);
+
+CREATE FUNCTION sort(_int4, text)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort_asc(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort_desc(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION uniq(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION idx(_int4, int4)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR # (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = idx
+);
+
+CREATE FUNCTION subarray(_int4, int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION subarray(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION intarray_push_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR + (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = intarray_push_elem
+);
+
+CREATE FUNCTION intarray_push_array(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR + (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ COMMUTATOR = +,
+ PROCEDURE = intarray_push_array
+);
+
+CREATE FUNCTION intarray_del_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR - (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = intarray_del_elem
+);
+
+CREATE FUNCTION intset_union_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR | (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = intset_union_elem
+);
+
+CREATE OPERATOR | (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ COMMUTATOR = |,
+ PROCEDURE = _int_union
+);
+
+CREATE FUNCTION intset_subtract(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR - (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = intset_subtract
+);
+
+CREATE OPERATOR & (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ COMMUTATOR = &,
+ PROCEDURE = _int_inter
+);
+--------------
+
+-- define the GiST support methods
+CREATE FUNCTION g_int_consistent(internal,_int4,smallint,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_compress(internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_decompress(internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_union(internal, internal)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_same(_int4, _int4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_int_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+-- Create the operator class for indexing
+
+CREATE OPERATOR CLASS gist__int_ops
+DEFAULT FOR TYPE _int4 USING gist AS
+ OPERATOR 3 &&,
+ OPERATOR 6 = (anyarray, anyarray),
+ OPERATOR 7 @>,
+ OPERATOR 8 <@,
+ OPERATOR 13 @,
+ OPERATOR 14 ~,
+ OPERATOR 20 @@ (_int4, query_int),
+ FUNCTION 1 g_int_consistent (internal, _int4, smallint, oid, internal),
+ FUNCTION 2 g_int_union (internal, internal),
+ FUNCTION 3 g_int_compress (internal, internal),
+ FUNCTION 4 g_int_decompress (internal, internal),
+ FUNCTION 5 g_int_penalty (internal, internal, internal),
+ FUNCTION 6 g_int_picksplit (internal, internal),
+ FUNCTION 7 g_int_same (_int4, _int4, internal),
+ FUNCTION 10 g_int_options (internal, boolean);
+
+
+---------------------------------------------
+-- intbig
+---------------------------------------------
+-- define the GiST support methods
+
+CREATE FUNCTION _intbig_in(cstring)
+RETURNS intbig_gkey
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _intbig_out(intbig_gkey)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE intbig_gkey (
+ INTERNALLENGTH = -1,
+ INPUT = _intbig_in,
+ OUTPUT = _intbig_out
+);
+
+CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_compress(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_decompress(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_penalty(internal,internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_picksplit(internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_union(internal, internal, internal)
+RETURNS intbig_gkey
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_intbig_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+-- register the opclass for indexing (not as default)
+
+CREATE OPERATOR CLASS gist__intbig_ops
+FOR TYPE _int4 USING gist
+AS
+ OPERATOR 3 &&,
+ OPERATOR 6 = (anyarray, anyarray),
+ OPERATOR 7 @>,
+ OPERATOR 8 <@,
+ OPERATOR 13 @,
+ OPERATOR 14 ~,
+ OPERATOR 20 @@ (_int4, query_int),
+ FUNCTION 1 g_intbig_consistent (internal, _int4, smallint, oid, internal, internal),
+ FUNCTION 2 g_intbig_union (internal, internal, internal),
+ FUNCTION 3 g_intbig_compress (internal, internal),
+ FUNCTION 4 g_intbig_decompress (internal, internal),
+ FUNCTION 5 g_intbig_penalty (internal, internal, internal, internal),
+ FUNCTION 6 g_intbig_picksplit (internal, internal, internal),
+ FUNCTION 7 g_intbig_same (intbig_gkey, intbig_gkey, internal, internal),
+ FUNCTION 10 g_intbig_options (internal, boolean),
+ STORAGE intbig_gkey;
+
+--GIN
+
+CREATE FUNCTION ginint4_queryextract(_int4, internal, int2, internal, internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ginint4_consistent(internal, int2, _int4, int4, internal, internal, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gin__int_ops
+FOR TYPE _int4 USING gin
+AS
+ OPERATOR 3 &&,
+ OPERATOR 6 = (anyarray, anyarray),
+ OPERATOR 7 @>,
+ OPERATOR 8 <@,
+ OPERATOR 13 @,
+ OPERATOR 14 ~,
+ OPERATOR 20 @@ (_int4, query_int),
+ FUNCTION 1 btint4cmp (int4, int4),
+ FUNCTION 2 ginarrayextract (anyarray, internal, internal),
+ FUNCTION 3 ginint4_queryextract (_int4, internal, int2, internal, internal, internal, internal),
+ FUNCTION 4 ginint4_consistent (internal, int2, _int4, int4, internal, internal, internal, internal),
+ STORAGE int4;
diff --git a/contrib/intarray/intarray.control b/contrib/intarray/intarray.control
index 7e50cc3..72952f7 100644
--- a/contrib/intarray/intarray.control
+++ b/contrib/intarray/intarray.control
@@ -1,5 +1,5 @@
# intarray extension
comment = 'functions, operators, and index support for 1-D arrays of integers'
-default_version = '1.2'
+default_version = '1.3'
module_pathname = '$libdir/_int'
relocatable = true
diff --git a/doc/src/sgml/intarray.sgml b/doc/src/sgml/intarray.sgml
index b633cf3..2540cc6 100644
--- a/doc/src/sgml/intarray.sgml
+++ b/doc/src/sgml/intarray.sgml
@@ -259,14 +259,22 @@
</para>
<para>
- Two GiST index operator classes are provided:
- <literal>gist__int_ops</literal> (used by default) is suitable for
+ Two parametrized GiST index operator classes are provided:
+ <literal>gist__int_ops(numranges)</literal> (used by default) is suitable for
small- to medium-size data sets, while
- <literal>gist__intbig_ops</literal> uses a larger signature and is more
+ <literal>gist__intbig_ops(siglen)</literal> uses a larger signature and is more
suitable for indexing large data sets (i.e., columns containing
a large number of distinct array values).
The implementation uses an RD-tree data structure with
built-in lossy compression.
+
+ Optional integer parameter <literal>numranges</literal> of
+ <literal>gist__int_ops</literal> determines
+ number of ranges in compressed array (100 by default).
+
+ Optional integer parameter <literal>siglen</literal> of
+ <literal>gist__intbig_ops</literal> determines
+ signature length in bytes (252 by default, 488 maximum).
</para>
<para>
--
2.7.4
[text/x-patch] 0006-Add-opclass-parameters-to-contrib-ltree-v02.patch (41.3K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/7-0006-Add-opclass-parameters-to-contrib-ltree-v02.patch)
download | inline diff:
From 5a69a859c8d066ae73394e67a13b9f535534c1dd Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 2 Feb 2018 03:36:13 +0300
Subject: [PATCH 6/9] Add opclass parameters to contrib/ltree
---
contrib/ltree/Makefile | 2 +-
contrib/ltree/_ltree_gist.c | 192 ++++++++++++++-------------
contrib/ltree/ltree--1.1--1.2.sql | 47 +++++++
contrib/ltree/ltree.control | 2 +-
contrib/ltree/ltree.h | 51 +++++---
contrib/ltree/ltree_gist.c | 265 +++++++++++++++++++++-----------------
doc/src/sgml/ltree.sgml | 16 ++-
7 files changed, 343 insertions(+), 232 deletions(-)
create mode 100644 contrib/ltree/ltree--1.1--1.2.sql
diff --git a/contrib/ltree/Makefile b/contrib/ltree/Makefile
index 416c8da..af031b3 100644
--- a/contrib/ltree/Makefile
+++ b/contrib/ltree/Makefile
@@ -6,7 +6,7 @@ OBJS = ltree_io.o ltree_op.o lquery_op.o _ltree_op.o crc32.o \
PG_CPPFLAGS = -DLOWER_NODE
EXTENSION = ltree
-DATA = ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
+DATA = ltree--1.1--1.2.sql ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
PGFILEDESC = "ltree - hierarchical label data type"
HEADERS = ltree.h
diff --git a/contrib/ltree/_ltree_gist.c b/contrib/ltree/_ltree_gist.c
index 28bf7ad..394f7ad 100644
--- a/contrib/ltree/_ltree_gist.c
+++ b/contrib/ltree/_ltree_gist.c
@@ -8,6 +8,7 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "crc32.h"
#include "ltree.h"
@@ -19,6 +20,7 @@ PG_FUNCTION_INFO_V1(_ltree_union);
PG_FUNCTION_INFO_V1(_ltree_penalty);
PG_FUNCTION_INFO_V1(_ltree_picksplit);
PG_FUNCTION_INFO_V1(_ltree_consistent);
+PG_FUNCTION_INFO_V1(_ltree_gist_options);
#define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
@@ -47,7 +49,7 @@ static const uint8 number_of_ones[256] = {
static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
{
int tlen = t->numlevel;
ltree_level *cur = LTREE_FIRST(t);
@@ -56,7 +58,7 @@ hashing(BITVECP sign, ltree *t)
while (tlen > 0)
{
hash = ltree_crc32_sz(cur->name, cur->len);
- AHASH(sign, hash);
+ AHASH(sign, hash, siglen);
cur = LEVEL_NEXT(cur);
tlen--;
}
@@ -67,12 +69,13 @@ _ltree_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval = entry;
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(1);
+ int siglen = options->siglen;
if (entry->leafkey)
{ /* ltree */
ltree_gist *key;
ArrayType *val = DatumGetArrayTypeP(entry->key);
- int32 len = LTG_HDRSIZE + ASIGLEN;
int num = ArrayGetNItems(ARR_NDIM(val), ARR_DIMS(val));
ltree *item = (ltree *) ARR_DATA_PTR(val);
@@ -85,14 +88,11 @@ _ltree_compress(PG_FUNCTION_ARGS)
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("array must not contain nulls")));
- key = (ltree_gist *) palloc0(len);
- SET_VARSIZE(key, len);
- key->flag = 0;
+ key = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
- MemSet(LTG_SIGN(key), 0, ASIGLEN);
while (num > 0)
{
- hashing(LTG_SIGN(key), item);
+ hashing(LTG_SIGN(key), item, siglen);
num--;
item = NEXTVAL(item);
}
@@ -104,22 +104,17 @@ _ltree_compress(PG_FUNCTION_ARGS)
}
else if (!LTG_ISALLTRUE(entry->key))
{
- int32 i,
- len;
+ int32 i;
ltree_gist *key;
-
BITVECP sign = LTG_SIGN(DatumGetPointer(entry->key));
- ALOOPBYTE
+ ALOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- len = LTG_HDRSIZE;
- key = (ltree_gist *) palloc0(len);
- SET_VARSIZE(key, len);
- key->flag = LTG_ALLTRUE;
+ key = ltree_gist_alloc(true, sign, siglen, NULL, NULL);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(key),
entry->rel, entry->page,
@@ -134,6 +129,8 @@ _ltree_same(PG_FUNCTION_ARGS)
ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
if (LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b))
*result = true;
@@ -148,7 +145,7 @@ _ltree_same(PG_FUNCTION_ARGS)
sb = LTG_SIGN(b);
*result = true;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -161,7 +158,7 @@ _ltree_same(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, ltree_gist *add)
+unionkey(BITVECP sbase, ltree_gist *add, int siglen)
{
int32 i;
BITVECP sadd = LTG_SIGN(add);
@@ -169,7 +166,7 @@ unionkey(BITVECP sbase, ltree_gist *add)
if (LTG_ISALLTRUE(add))
return 1;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
sbase[i] |= sadd[i];
return 0;
}
@@ -179,52 +176,46 @@ _ltree_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- ABITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- ltree_gist *result;
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
+ int32 i;
+ ltree_gist *result = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
+ BITVECP base = LTG_SIGN(result);
- MemSet((void *) base, 0, sizeof(ABITVEC));
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = LTG_ALLTRUE;
+ result->flag |= LTG_ALLTRUE;
+ SET_VARSIZE(result, LTG_HDRSIZE);
break;
}
}
- len = LTG_HDRSIZE + ((flag & LTG_ALLTRUE) ? 0 : ASIGLEN);
- result = (ltree_gist *) palloc0(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!LTG_ISALLTRUE(result))
- memcpy((void *) LTG_SIGN(result), (void *) base, sizeof(ABITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
diff,
dist = 0;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
{
diff = (unsigned char) (a[i] ^ b[i]);
dist += number_of_ones[diff];
@@ -233,19 +224,19 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(ltree_gist *a, ltree_gist *b)
+hemdist(ltree_gist *a, ltree_gist *b, int siglen)
{
if (LTG_ISALLTRUE(a))
{
if (LTG_ISALLTRUE(b))
return 0;
else
- return ASIGLENBIT - sizebitvec(LTG_SIGN(b));
+ return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(b), siglen);
}
else if (LTG_ISALLTRUE(b))
- return ASIGLENBIT - sizebitvec(LTG_SIGN(a));
+ return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(a), siglen);
- return hemdistsign(LTG_SIGN(a), LTG_SIGN(b));
+ return hemdistsign(LTG_SIGN(a), LTG_SIGN(b), siglen);
}
@@ -255,8 +246,10 @@ _ltree_penalty(PG_FUNCTION_ARGS)
ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
- *penalty = hemdist(origval, newval);
+ *penalty = hemdist(origval, newval, siglen);
PG_RETURN_POINTER(penalty);
}
@@ -277,6 +270,8 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber k,
j;
ltree_gist *datum_l,
@@ -309,7 +304,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
_k = GETENTRY(entryvec, k);
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
- size_waste = hemdist(_k, GETENTRY(entryvec, j));
+ size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -331,32 +326,13 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)))
- {
- datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE);
- SET_VARSIZE(datum_l, LTG_HDRSIZE);
- datum_l->flag = LTG_ALLTRUE;
- }
- else
- {
- datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
- SET_VARSIZE(datum_l, LTG_HDRSIZE + ASIGLEN);
- datum_l->flag = 0;
- memcpy((void *) LTG_SIGN(datum_l), (void *) LTG_SIGN(GETENTRY(entryvec, seed_1)), sizeof(ABITVEC));
- }
- if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)))
- {
- datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE);
- SET_VARSIZE(datum_r, LTG_HDRSIZE);
- datum_r->flag = LTG_ALLTRUE;
- }
- else
- {
- datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
- SET_VARSIZE(datum_r, LTG_HDRSIZE + ASIGLEN);
- datum_r->flag = 0;
- memcpy((void *) LTG_SIGN(datum_r), (void *) LTG_SIGN(GETENTRY(entryvec, seed_2)), sizeof(ABITVEC));
- }
+ datum_l = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)),
+ LTG_SIGN(GETENTRY(entryvec, seed_1)),
+ siglen, NULL, NULL);
+
+ datum_r = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)),
+ LTG_SIGN(GETENTRY(entryvec, seed_2)),
+ siglen, NULL, NULL);
maxoff = OffsetNumberNext(maxoff);
/* sort before ... */
@@ -365,8 +341,8 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
{
costvector[j - 1].pos = j;
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -390,20 +366,20 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
continue;
}
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
{
if (LTG_ISALLTRUE(datum_l) || LTG_ISALLTRUE(_j))
{
if (!LTG_ISALLTRUE(datum_l))
- MemSet((void *) union_l, 0xff, sizeof(ABITVEC));
+ MemSet((void *) union_l, 0xff, siglen);
}
else
{
ptr = LTG_SIGN(_j);
- ALOOPBYTE
+ ALOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -414,12 +390,12 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
if (LTG_ISALLTRUE(datum_r) || LTG_ISALLTRUE(_j))
{
if (!LTG_ISALLTRUE(datum_r))
- MemSet((void *) union_r, 0xff, sizeof(ABITVEC));
+ MemSet((void *) union_r, 0xff, siglen);
}
else
{
ptr = LTG_SIGN(_j);
- ALOOPBYTE
+ ALOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -436,7 +412,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
}
static bool
-gist_te(ltree_gist *key, ltree *query)
+gist_te(ltree_gist *key, ltree *query, int siglen)
{
ltree_level *curq = LTREE_FIRST(query);
BITVECP sign = LTG_SIGN(key);
@@ -449,7 +425,7 @@ gist_te(ltree_gist *key, ltree *query)
while (qlen > 0)
{
hv = ltree_crc32_sz(curq->name, curq->len);
- if (!GETBIT(sign, AHASHVAL(hv)))
+ if (!GETBIT(sign, AHASHVAL(hv, siglen)))
return false;
curq = LEVEL_NEXT(curq);
qlen--;
@@ -458,27 +434,40 @@ gist_te(ltree_gist *key, ltree *query)
return true;
}
+typedef struct LtreeSignature
+{
+ BITVECP sign;
+ int siglen;
+} LtreeSignature;
+
static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
{
- return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, AHASHVAL(val->val)) : true;
+ LtreeSignature *sig = cxt;
+
+ return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, AHASHVAL(val->val, sig->siglen)) : true;
}
static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
{
+ LtreeSignature sig;
+
if (LTG_ISALLTRUE(key))
return true;
+ sig.sign = LTG_SIGN(key);
+ sig.siglen = siglen;
+
return ltree_execute(
GETQUERY(query),
- (void *) LTG_SIGN(key), false,
+ &sig, false,
checkcondition_bit
);
}
static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
{
lquery_level *curq = LQUERY_FIRST(query);
BITVECP sign = LTG_SIGN(key);
@@ -497,7 +486,7 @@ gist_qe(ltree_gist *key, lquery *query)
while (vlen > 0)
{
- if (GETBIT(sign, AHASHVAL(curv->val)))
+ if (GETBIT(sign, AHASHVAL(curv->val, siglen)))
{
isexist = true;
break;
@@ -517,7 +506,7 @@ gist_qe(ltree_gist *key, lquery *query)
}
static bool
-_arrq_cons(ltree_gist *key, ArrayType *_query)
+_arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
{
lquery *query = (lquery *) ARR_DATA_PTR(_query);
int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -533,7 +522,7 @@ _arrq_cons(ltree_gist *key, ArrayType *_query)
while (num > 0)
{
- if (gist_qe(key, query))
+ if (gist_qe(key, query, siglen))
return true;
num--;
query = (lquery *) NEXTVAL(query);
@@ -550,6 +539,8 @@ _ltree_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
bool res = false;
@@ -560,19 +551,19 @@ _ltree_consistent(PG_FUNCTION_ARGS)
{
case 10:
case 11:
- res = gist_te(key, (ltree *) query);
+ res = gist_te(key, (ltree *) query, siglen);
break;
case 12:
case 13:
- res = gist_qe(key, (lquery *) query);
+ res = gist_qe(key, (lquery *) query, siglen);
break;
case 14:
case 15:
- res = gist_qtxt(key, (ltxtquery *) query);
+ res = gist_qtxt(key, (ltxtquery *) query, siglen);
break;
case 16:
case 17:
- res = _arrq_cons(key, (ArrayType *) query);
+ res = _arrq_cons(key, (ArrayType *) query, siglen);
break;
default:
/* internal error */
@@ -581,3 +572,20 @@ _ltree_consistent(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(res);
}
+
+Datum
+_ltree_gist_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(LtreeGistOptions, siglen) };
+ LtreeGistOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(LtreeGistOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/contrib/ltree/ltree--1.1--1.2.sql b/contrib/ltree/ltree--1.1--1.2.sql
new file mode 100644
index 0000000..d002e2b
--- /dev/null
+++ b/contrib/ltree/ltree--1.1--1.2.sql
@@ -0,0 +1,47 @@
+/* contrib/ltree/ltree--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION ltree UPDATE TO '1.2'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('ltree_compress(internal)', '{internal,internal}'),
+('ltree_decompress(internal)', '{internal,internal}'),
+('ltree_same(ltree_gist,ltree_gist,internal)', '{ltree_gist,ltree_gist,internal,internal}'),
+('ltree_union(internal,internal)', '{internal,internal,internal}'),
+('ltree_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('ltree_picksplit(internal,internal)', '{internal,internal,internal}'),
+('ltree_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}'),
+('_ltree_compress(internal)', '{internal,internal}'),
+('_ltree_same(ltree_gist,ltree_gist,internal)', '{ltree_gist,ltree_gist,internal,internal}'),
+('_ltree_union(internal,internal)', '{internal,internal,internal}'),
+('_ltree_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('_ltree_picksplit(internal,internal)', '{internal,internal,internal}'),
+('_ltree_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION ltree_gist_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'ltree_gist_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_gist_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', '_ltree_gist_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_ltree_ops USING gist
+ADD FUNCTION 10 (ltree) ltree_gist_options (internal, boolean);
+
+ALTER OPERATOR FAMILY gist__ltree_ops USING gist
+ADD FUNCTION 10 (_ltree) _ltree_gist_options (internal, boolean);
+
diff --git a/contrib/ltree/ltree.control b/contrib/ltree/ltree.control
index 03c3fb1..61c8cdf 100644
--- a/contrib/ltree/ltree.control
+++ b/contrib/ltree/ltree.control
@@ -1,5 +1,5 @@
# ltree extension
comment = 'data type for hierarchical tree-like structures'
-default_version = '1.1'
+default_version = '1.2'
module_pathname = '$libdir/ltree'
relocatable = true
diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h
index e4b8c84..6247564 100644
--- a/contrib/ltree/ltree.h
+++ b/contrib/ltree/ltree.h
@@ -183,15 +183,16 @@ int ltree_strncasecmp(const char *a, const char *b, size_t s);
/* GiST support for ltree */
+#define SIGLEN_MAX (122 * sizeof(int32))
+#define SIGLEN_DEFAULT (2 * sizeof(int32))
#define BITBYTE 8
-#define SIGLENINT 2
-#define SIGLEN ( sizeof(int32)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
-typedef unsigned char BITVEC[SIGLEN];
+#define SIGLEN (sizeof(int32) * SIGLENINT)
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
+
typedef unsigned 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) / BITBYTE ) ) )
#define GETBITBYTE(x,i) ( ((unsigned char)(x)) >> i & 0x01 )
@@ -199,8 +200,8 @@ typedef unsigned char *BITVECP;
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITBYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 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))
/*
* type of index key for ltree. Tree are combined B-Tree and R-Tree
@@ -230,26 +231,34 @@ typedef struct
#define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE )
#define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE )
#define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT )
-#define LTG_LNODE(x) ( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) )
-#define LTG_RENODE(x) ( (ltree*)( ((char*)LTG_LNODE(x)) + VARSIZE(LTG_LNODE(x))) )
-#define LTG_RNODE(x) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x) : LTG_RENODE(x) )
+#define LTG_LNODE(x, siglen) ( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : (siglen) ) ) )
+#define LTG_RENODE(x, siglen) ( (ltree*)( ((char*)LTG_LNODE(x, siglen)) + VARSIZE(LTG_LNODE(x, siglen))) )
+#define LTG_RNODE(x, siglen) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x, siglen) : LTG_RENODE(x, siglen) )
-#define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) )
-#define LTG_GETRNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x) )
+#define LTG_GETLNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x, siglen) )
+#define LTG_GETRNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x, siglen) )
+extern ltree_gist *ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+ ltree *left, ltree *right);
/* GiST support for ltree[] */
-#define ASIGLENINT (7)
-#define ASIGLEN (sizeof(int32)*ASIGLENINT)
-#define ASIGLENBIT (ASIGLEN*BITBYTE)
-typedef unsigned char ABITVEC[ASIGLEN];
+#define LTREE_ASIGLEN_DEFAULT (7 * sizeof(int32))
+#define LTREE_ASIGLEN_MAX (122 * sizeof(int32))
+#define ASIGLENBIT(siglen) ((siglen) * BITBYTE)
+
+#define ALOOPBYTE(siglen) \
+ for (i = 0; i < (siglen); i++)
-#define ALOOPBYTE \
- for(i=0;i<ASIGLEN;i++)
+#define AHASHVAL(val, siglen) (((unsigned int)(val)) % ASIGLENBIT(siglen))
+#define AHASH(sign, val, siglen) SETBIT((sign), AHASHVAL(val, siglen))
-#define AHASHVAL(val) (((unsigned int)(val)) % ASIGLENBIT)
-#define AHASH(sign, val) SETBIT((sign), AHASHVAL(val))
+/* gist_ltree_ops and gist__ltree_ops opclass options */
+typedef struct LtreeGistOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} LtreeGistOptions;
/* type of key is the same to ltree_gist */
diff --git a/contrib/ltree/ltree_gist.c b/contrib/ltree/ltree_gist.c
index 12aa8ff..9847b83 100644
--- a/contrib/ltree/ltree_gist.c
+++ b/contrib/ltree/ltree_gist.c
@@ -6,11 +6,13 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "crc32.h"
#include "ltree.h"
#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
+#define ISEQ(a,b) ( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
PG_FUNCTION_INFO_V1(ltree_gist_in);
PG_FUNCTION_INFO_V1(ltree_gist_out);
@@ -33,6 +35,47 @@ ltree_gist_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+ltree_gist *
+ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+ ltree *left, ltree *right)
+{
+ int32 size = LTG_HDRSIZE + (isalltrue ? 0 : siglen) +
+ (left ? VARSIZE(left) + (right ? VARSIZE(right) : 0) : 0);
+ ltree_gist *result = palloc(size);
+
+ SET_VARSIZE(result, size);
+
+ if (siglen)
+ {
+ result->flag = 0;
+
+ if (isalltrue)
+ result->flag |= LTG_ALLTRUE;
+ else if (sign)
+ memcpy(LTG_SIGN(result), sign, siglen);
+ else
+ memset(LTG_SIGN(result), 0, siglen);
+
+ if (left)
+ {
+ memcpy(LTG_LNODE(result, siglen), left, VARSIZE(left));
+
+ if (!right || left == right || ISEQ(left, right))
+ result->flag |= LTG_NORIGHT;
+ else
+ memcpy(LTG_RNODE(result, siglen), right, VARSIZE(right));
+ }
+ }
+ else
+ {
+ Assert(left);
+ result->flag = LTG_ONENODE;
+ memcpy(LTG_NODE(result), left, VARSIZE(left));
+ }
+
+ return result;
+}
+
PG_FUNCTION_INFO_V1(ltree_compress);
PG_FUNCTION_INFO_V1(ltree_decompress);
PG_FUNCTION_INFO_V1(ltree_same);
@@ -40,8 +83,8 @@ PG_FUNCTION_INFO_V1(ltree_union);
PG_FUNCTION_INFO_V1(ltree_penalty);
PG_FUNCTION_INFO_V1(ltree_picksplit);
PG_FUNCTION_INFO_V1(ltree_consistent);
+PG_FUNCTION_INFO_V1(ltree_gist_options);
-#define ISEQ(a,b) ( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
#define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
Datum
@@ -52,14 +95,8 @@ ltree_compress(PG_FUNCTION_ARGS)
if (entry->leafkey)
{ /* ltree */
- ltree_gist *key;
ltree *val = DatumGetLtreeP(entry->key);
- int32 len = LTG_HDRSIZE + VARSIZE(val);
-
- key = (ltree_gist *) palloc0(len);
- SET_VARSIZE(key, len);
- key->flag = LTG_ONENODE;
- memcpy((void *) LTG_NODE(key), (void *) val, VARSIZE(val));
+ ltree_gist *key = ltree_gist_alloc(false, NULL, 0, val, 0);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(key),
@@ -93,6 +130,8 @@ ltree_same(PG_FUNCTION_ARGS)
ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
*result = false;
if (LTG_ISONENODE(a) != LTG_ISONENODE(b))
@@ -109,15 +148,15 @@ ltree_same(PG_FUNCTION_ARGS)
if (LTG_ISALLTRUE(a) != LTG_ISALLTRUE(b))
PG_RETURN_POINTER(result);
- if (!ISEQ(LTG_LNODE(a), LTG_LNODE(b)))
+ if (!ISEQ(LTG_LNODE(a, siglen), LTG_LNODE(b, siglen)))
PG_RETURN_POINTER(result);
- if (!ISEQ(LTG_RNODE(a), LTG_RNODE(b)))
+ if (!ISEQ(LTG_RNODE(a, siglen), LTG_RNODE(b, siglen)))
PG_RETURN_POINTER(result);
*result = true;
if (!LTG_ISALLTRUE(a))
{
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -132,7 +171,7 @@ ltree_same(PG_FUNCTION_ARGS)
}
static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
{
int tlen = t->numlevel;
ltree_level *cur = LTREE_FIRST(t);
@@ -141,7 +180,7 @@ hashing(BITVECP sign, ltree *t)
while (tlen > 0)
{
hash = ltree_crc32_sz(cur->name, cur->len);
- HASH(sign, hash);
+ HASH(sign, hash, siglen);
cur = LEVEL_NEXT(cur);
tlen--;
}
@@ -152,7 +191,9 @@ ltree_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
+ BITVECP base = palloc0(siglen);
int32 i,
j;
ltree_gist *result,
@@ -161,16 +202,14 @@ ltree_union(PG_FUNCTION_ARGS)
*right = NULL,
*curtree;
bool isalltrue = false;
- bool isleqr;
- MemSet((void *) base, 0, sizeof(BITVEC));
for (j = 0; j < entryvec->n; j++)
{
cur = GETENTRY(entryvec, j);
if (LTG_ISONENODE(cur))
{
curtree = LTG_NODE(cur);
- hashing(base, curtree);
+ hashing(base, curtree, siglen);
if (!left || ltree_compare(left, curtree) > 0)
left = curtree;
if (!right || ltree_compare(right, curtree) < 0)
@@ -184,14 +223,14 @@ ltree_union(PG_FUNCTION_ARGS)
{
BITVECP sc = LTG_SIGN(cur);
- LOOPBYTE
+ LOOPBYTE(siglen)
((unsigned char *) base)[i] |= sc[i];
}
- curtree = LTG_LNODE(cur);
+ curtree = LTG_LNODE(cur, siglen);
if (!left || ltree_compare(left, curtree) > 0)
left = curtree;
- curtree = LTG_RNODE(cur);
+ curtree = LTG_RNODE(cur, siglen);
if (!right || ltree_compare(right, curtree) < 0)
right = curtree;
}
@@ -200,7 +239,7 @@ ltree_union(PG_FUNCTION_ARGS)
if (isalltrue == false)
{
isalltrue = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) base)[i] != 0xff)
{
@@ -210,23 +249,9 @@ ltree_union(PG_FUNCTION_ARGS)
}
}
- isleqr = (left == right || ISEQ(left, right)) ? true : false;
- *size = LTG_HDRSIZE + ((isalltrue) ? 0 : SIGLEN) + VARSIZE(left) + ((isleqr) ? 0 : VARSIZE(right));
-
- result = (ltree_gist *) palloc0(*size);
- SET_VARSIZE(result, *size);
- result->flag = 0;
+ result = ltree_gist_alloc(isalltrue, base, siglen, left, right);
- if (isalltrue)
- result->flag |= LTG_ALLTRUE;
- else
- memcpy((void *) LTG_SIGN(result), base, SIGLEN);
-
- memcpy((void *) LTG_LNODE(result), (void *) left, VARSIZE(left));
- if (isleqr)
- result->flag |= LTG_NORIGHT;
- else
- memcpy((void *) LTG_RNODE(result), (void *) right, VARSIZE(right));
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -237,11 +262,13 @@ ltree_penalty(PG_FUNCTION_ARGS)
ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
int32 cmpr,
cmpl;
- cmpl = ltree_compare(LTG_GETLNODE(origval), LTG_GETLNODE(newval));
- cmpr = ltree_compare(LTG_GETRNODE(newval), LTG_GETRNODE(origval));
+ cmpl = ltree_compare(LTG_GETLNODE(origval, siglen), LTG_GETLNODE(newval, siglen));
+ cmpr = ltree_compare(LTG_GETRNODE(newval, siglen), LTG_GETRNODE(origval, siglen));
*penalty = Max(cmpl, 0) + Max(cmpr, 0);
@@ -270,26 +297,24 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber j;
int32 i;
RIX *array;
OffsetNumber maxoff;
int nbytes;
- int size;
ltree *lu_l,
*lu_r,
*ru_l,
*ru_r;
ltree_gist *lu,
*ru;
- BITVEC ls,
- rs;
+ BITVECP ls = palloc0(siglen),
+ rs = palloc0(siglen);
bool lisat = false,
- risat = false,
- isleqr;
+ risat = false;
- memset((void *) ls, 0, sizeof(BITVEC));
- memset((void *) rs, 0, sizeof(BITVEC));
maxoff = entryvec->n - 1;
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
v->spl_left = (OffsetNumber *) palloc(nbytes);
@@ -303,7 +328,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
array[j].index = j;
lu = GETENTRY(entryvec, j); /* use as tmp val */
- array[j].r = LTG_GETLNODE(lu);
+ array[j].r = LTG_GETLNODE(lu, siglen);
}
qsort((void *) &array[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1,
@@ -317,10 +342,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
v->spl_left[v->spl_nleft] = array[j].index;
v->spl_nleft++;
- if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu), lu_r) > 0)
- lu_r = LTG_GETRNODE(lu);
+ if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), lu_r) > 0)
+ lu_r = LTG_GETRNODE(lu, siglen);
if (LTG_ISONENODE(lu))
- hashing(ls, LTG_NODE(lu));
+ hashing(ls, LTG_NODE(lu), siglen);
else
{
if (lisat || LTG_ISALLTRUE(lu))
@@ -329,7 +354,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
BITVECP sc = LTG_SIGN(lu);
- LOOPBYTE
+ LOOPBYTE(siglen)
((unsigned char *) ls)[i] |= sc[i];
}
}
@@ -338,10 +363,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
v->spl_right[v->spl_nright] = array[j].index;
v->spl_nright++;
- if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu), ru_r) > 0)
- ru_r = LTG_GETRNODE(lu);
+ if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), ru_r) > 0)
+ ru_r = LTG_GETRNODE(lu, siglen);
if (LTG_ISONENODE(lu))
- hashing(rs, LTG_NODE(lu));
+ hashing(rs, LTG_NODE(lu), siglen);
else
{
if (risat || LTG_ISALLTRUE(lu))
@@ -350,7 +375,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
BITVECP sc = LTG_SIGN(lu);
- LOOPBYTE
+ LOOPBYTE(siglen)
((unsigned char *) rs)[i] |= sc[i];
}
}
@@ -360,7 +385,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
if (lisat == false)
{
lisat = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) ls)[i] != 0xff)
{
@@ -373,7 +398,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
if (risat == false)
{
risat = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) rs)[i] != 0xff)
{
@@ -383,38 +408,14 @@ ltree_picksplit(PG_FUNCTION_ARGS)
}
}
- lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index));
- isleqr = (lu_l == lu_r || ISEQ(lu_l, lu_r)) ? true : false;
- size = LTG_HDRSIZE + ((lisat) ? 0 : SIGLEN) + VARSIZE(lu_l) + ((isleqr) ? 0 : VARSIZE(lu_r));
- lu = (ltree_gist *) palloc0(size);
- SET_VARSIZE(lu, size);
- lu->flag = 0;
- if (lisat)
- lu->flag |= LTG_ALLTRUE;
- else
- memcpy((void *) LTG_SIGN(lu), ls, SIGLEN);
- memcpy((void *) LTG_LNODE(lu), (void *) lu_l, VARSIZE(lu_l));
- if (isleqr)
- lu->flag |= LTG_NORIGHT;
- else
- memcpy((void *) LTG_RNODE(lu), (void *) lu_r, VARSIZE(lu_r));
+ lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index), siglen);
+ lu = ltree_gist_alloc(lisat, ls, siglen, lu_l, lu_r);
+ ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index), siglen);
+ ru = ltree_gist_alloc(risat, rs, siglen, ru_l, ru_r);
- ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index));
- isleqr = (ru_l == ru_r || ISEQ(ru_l, ru_r)) ? true : false;
- size = LTG_HDRSIZE + ((risat) ? 0 : SIGLEN) + VARSIZE(ru_l) + ((isleqr) ? 0 : VARSIZE(ru_r));
- ru = (ltree_gist *) palloc0(size);
- SET_VARSIZE(ru, size);
- ru->flag = 0;
- if (risat)
- ru->flag |= LTG_ALLTRUE;
- else
- memcpy((void *) LTG_SIGN(ru), rs, SIGLEN);
- memcpy((void *) LTG_LNODE(ru), (void *) ru_l, VARSIZE(ru_l));
- if (isleqr)
- ru->flag |= LTG_NORIGHT;
- else
- memcpy((void *) LTG_RNODE(ru), (void *) ru_r, VARSIZE(ru_r));
+ pfree(ls);
+ pfree(rs);
v->spl_ldatum = PointerGetDatum(lu);
v->spl_rdatum = PointerGetDatum(ru);
@@ -423,7 +424,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
}
static bool
-gist_isparent(ltree_gist *key, ltree *query)
+gist_isparent(ltree_gist *key, ltree *query, int siglen)
{
int32 numlevel = query->numlevel;
int i;
@@ -431,7 +432,8 @@ gist_isparent(ltree_gist *key, ltree *query)
for (i = query->numlevel; i >= 0; i--)
{
query->numlevel = i;
- if (ltree_compare(query, LTG_GETLNODE(key)) >= 0 && ltree_compare(query, LTG_GETRNODE(key)) <= 0)
+ if (ltree_compare(query, LTG_GETLNODE(key, siglen)) >= 0 &&
+ ltree_compare(query, LTG_GETRNODE(key, siglen)) <= 0)
{
query->numlevel = numlevel;
return true;
@@ -452,10 +454,10 @@ copy_ltree(ltree *src)
}
static bool
-gist_ischild(ltree_gist *key, ltree *query)
+gist_ischild(ltree_gist *key, ltree *query, int siglen)
{
- ltree *left = copy_ltree(LTG_GETLNODE(key));
- ltree *right = copy_ltree(LTG_GETRNODE(key));
+ ltree *left = copy_ltree(LTG_GETLNODE(key, siglen));
+ ltree *right = copy_ltree(LTG_GETRNODE(key, siglen));
bool res = true;
if (left->numlevel > query->numlevel)
@@ -477,7 +479,7 @@ gist_ischild(ltree_gist *key, ltree *query)
}
static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
{
lquery_level *curq = LQUERY_FIRST(query);
BITVECP sign = LTG_SIGN(key);
@@ -496,7 +498,7 @@ gist_qe(ltree_gist *key, lquery *query)
while (vlen > 0)
{
- if (GETBIT(sign, HASHVAL(curv->val)))
+ if (GETBIT(sign, HASHVAL(curv->val, siglen)))
{
isexist = true;
break;
@@ -545,41 +547,54 @@ gist_tqcmp(ltree *t, lquery *q)
}
static bool
-gist_between(ltree_gist *key, lquery *query)
+gist_between(ltree_gist *key, lquery *query, int siglen)
{
if (query->firstgood == 0)
return true;
- if (gist_tqcmp(LTG_GETLNODE(key), query) > 0)
+ if (gist_tqcmp(LTG_GETLNODE(key, siglen), query) > 0)
return false;
- if (gist_tqcmp(LTG_GETRNODE(key), query) < 0)
+ if (gist_tqcmp(LTG_GETRNODE(key, siglen), query) < 0)
return false;
return true;
}
+typedef struct LtreeSignature
+{
+ BITVECP sign;
+ int siglen;
+} LtreeSignature;
+
static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
{
- return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, HASHVAL(val->val)) : true;
+ LtreeSignature *sig = cxt;
+
+ return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, HASHVAL(val->val, sig->siglen)) : true;
}
static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
{
+ LtreeSignature sig;
+
if (LTG_ISALLTRUE(key))
return true;
+ sig.sign = LTG_SIGN(key);
+ sig.siglen = siglen;
+
return ltree_execute(
GETQUERY(query),
- (void *) LTG_SIGN(key), false,
+ &sig, false,
checkcondition_bit
);
}
static bool
-arrq_cons(ltree_gist *key, ArrayType *_query)
+arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
{
lquery *query = (lquery *) ARR_DATA_PTR(_query);
int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -595,7 +610,7 @@ arrq_cons(ltree_gist *key, ArrayType *_query)
while (num > 0)
{
- if (gist_qe(key, query) && gist_between(key, query))
+ if (gist_qe(key, query, siglen) && gist_between(key, query, siglen))
return true;
num--;
query = NEXTVAL(query);
@@ -611,6 +626,8 @@ ltree_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
void *query = NULL;
bool res = false;
@@ -625,11 +642,11 @@ ltree_consistent(PG_FUNCTION_ARGS)
res = (GIST_LEAF(entry)) ?
(ltree_compare((ltree *) query, LTG_NODE(key)) > 0)
:
- (ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+ (ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
break;
case BTLessEqualStrategyNumber:
query = PG_GETARG_LTREE_P(1);
- res = (ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+ res = (ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
break;
case BTEqualStrategyNumber:
query = PG_GETARG_LTREE_P(1);
@@ -637,35 +654,35 @@ ltree_consistent(PG_FUNCTION_ARGS)
res = (ltree_compare((ltree *) query, LTG_NODE(key)) == 0);
else
res = (
- ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0
+ ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0
&&
- ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0
+ ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0
);
break;
case BTGreaterEqualStrategyNumber:
query = PG_GETARG_LTREE_P(1);
- res = (ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+ res = (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
break;
case BTGreaterStrategyNumber:
query = PG_GETARG_LTREE_P(1);
res = (GIST_LEAF(entry)) ?
- (ltree_compare((ltree *) query, LTG_GETRNODE(key)) < 0)
+ (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) < 0)
:
- (ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+ (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
break;
case 10:
query = PG_GETARG_LTREE_P_COPY(1);
res = (GIST_LEAF(entry)) ?
inner_isparent((ltree *) query, LTG_NODE(key))
:
- gist_isparent(key, (ltree *) query);
+ gist_isparent(key, (ltree *) query, siglen);
break;
case 11:
query = PG_GETARG_LTREE_P(1);
res = (GIST_LEAF(entry)) ?
inner_isparent(LTG_NODE(key), (ltree *) query)
:
- gist_ischild(key, (ltree *) query);
+ gist_ischild(key, (ltree *) query, siglen);
break;
case 12:
case 13:
@@ -676,7 +693,8 @@ ltree_consistent(PG_FUNCTION_ARGS)
PointerGetDatum((lquery *) query)
));
else
- res = (gist_qe(key, (lquery *) query) && gist_between(key, (lquery *) query));
+ res = (gist_qe(key, (lquery *) query, siglen) &&
+ gist_between(key, (lquery *) query, siglen));
break;
case 14:
case 15:
@@ -687,7 +705,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
PointerGetDatum((ltxtquery *) query)
));
else
- res = gist_qtxt(key, (ltxtquery *) query);
+ res = gist_qtxt(key, (ltxtquery *) query, siglen);
break;
case 16:
case 17:
@@ -698,7 +716,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
PointerGetDatum((ArrayType *) query)
));
else
- res = arrq_cons(key, (ArrayType *) query);
+ res = arrq_cons(key, (ArrayType *) query, siglen);
break;
default:
/* internal error */
@@ -708,3 +726,20 @@ ltree_consistent(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(res);
}
+
+Datum
+ltree_gist_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(LtreeGistOptions, siglen) };
+ LtreeGistOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(LtreeGistOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index 3ddd335..dd37445 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -500,11 +500,17 @@ Europe & Russia*@ & !Transportation
<literal>@</literal>, <literal>~</literal>, <literal>?</literal>
</para>
<para>
- Example of creating such an index:
+ Example of creating such an index with a default signature length of 8 bytes:
</para>
<programlisting>
CREATE INDEX path_gist_idx ON test USING GIST (path);
</programlisting>
+ <para>
+ Example of creating such an index with a signature length of 100 bytes:
+ </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
+</programlisting>
</listitem>
<listitem>
<para>
@@ -513,12 +519,18 @@ CREATE INDEX path_gist_idx ON test USING GIST (path);
<literal>@</literal>, <literal>~</literal>, <literal>?</literal>
</para>
<para>
- Example of creating such an index:
+ Example of creating such an index with a default signature length of 28 bytes:
</para>
<programlisting>
CREATE INDEX path_gist_idx ON test USING GIST (array_path);
</programlisting>
<para>
+ Example of creating such an index with a signature length of 100 bytes:
+ </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100));
+</programlisting>
+ <para>
Note: This index type is lossy.
</para>
</listitem>
--
2.7.4
[text/x-patch] 0007-Add-opclass-parameters-to-contrib-pg_trgm-v02.patch (23.0K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/8-0007-Add-opclass-parameters-to-contrib-pg_trgm-v02.patch)
download | inline diff:
From 585da1d8f6dd8f72a3b5f3d8d6ff0cdb4db90655 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Sat, 3 Feb 2018 02:12:56 +0300
Subject: [PATCH 7/9] Add opclass parameters to contrib/pg_trgm
---
contrib/pg_trgm/Makefile | 2 +-
contrib/pg_trgm/pg_trgm--1.4--1.5.sql | 33 +++++
contrib/pg_trgm/pg_trgm.control | 2 +-
contrib/pg_trgm/trgm.h | 17 ++-
contrib/pg_trgm/trgm_gist.c | 234 +++++++++++++++++++---------------
doc/src/sgml/pgtrgm.sgml | 9 ++
6 files changed, 184 insertions(+), 113 deletions(-)
create mode 100644 contrib/pg_trgm/pg_trgm--1.4--1.5.sql
diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index dfecc2a..6f452b5 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -4,7 +4,7 @@ MODULE_big = pg_trgm
OBJS = trgm_op.o trgm_gist.o trgm_gin.o trgm_regexp.o $(WIN32RES)
EXTENSION = pg_trgm
-DATA = pg_trgm--1.3--1.4.sql \
+DATA = pg_trgm--1.4--1.5.sql pg_trgm--1.3--1.4.sql \
pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql pg_trgm--1.1--1.2.sql \
pg_trgm--1.0--1.1.sql pg_trgm--unpackaged--1.0.sql
PGFILEDESC = "pg_trgm - trigram matching"
diff --git a/contrib/pg_trgm/pg_trgm--1.4--1.5.sql b/contrib/pg_trgm/pg_trgm--1.4--1.5.sql
new file mode 100644
index 0000000..9ebb4f1
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.4--1.5.sql
@@ -0,0 +1,33 @@
+/* contrib/pg_trgm/pg_trgm--1.5--1.5.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.5'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('gtrgm_compress(internal)', '{internal,internal}'),
+('gtrgm_decompress(internal)', '{internal,internal}'),
+('gtrgm_same(gtrgm,gtrgm,internal)', '{gtrgm,gtrgm,internal,internal}'),
+('gtrgm_union(internal,internal)', '{internal,internal,internal}'),
+('gtrgm_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('gtrgm_picksplit(internal,internal)', '{internal,internal,internal}'),
+('gtrgm_consistent(internal,text,smallint,oid,internal)', '{internal,text,smallint,oid,internal,internal}'),
+('gtrgm_distance(internal,text,smallint,oid,internal)', '{internal,text,smallint,oid,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION gtrgm_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'gtrgm_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_trgm_ops USING gist
+ADD FUNCTION 10 (text) gtrgm_options (internal, boolean);
diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control
index 3e325dd..d633eac 100644
--- a/contrib/pg_trgm/pg_trgm.control
+++ b/contrib/pg_trgm/pg_trgm.control
@@ -1,5 +1,5 @@
# pg_trgm extension
comment = 'text similarity measurement and index searching based on trigrams'
-default_version = '1.4'
+default_version = '1.5'
module_pathname = '$libdir/pg_trgm'
relocatable = true
diff --git a/contrib/pg_trgm/trgm.h b/contrib/pg_trgm/trgm.h
index f0ab50d..acdd83b 100644
--- a/contrib/pg_trgm/trgm.h
+++ b/contrib/pg_trgm/trgm.h
@@ -73,17 +73,16 @@ typedef struct
#define TRGMHDRSIZE (VARHDRSZ + sizeof(uint8))
/* gist */
+#define SIGLEN_DEFAULT (sizeof(int) * 3)
+#define SIGLEN_MAX (sizeof(int) * 122) /* key will toast, so very slow!!! */
#define BITBYTE 8
-#define SIGLENINT 3 /* >122 => key will toast, so very slow!!! */
-#define SIGLEN ( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE - 1) /* see makesign */
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE - 1) /* see makesign */
-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) / BITBYTE ) ) )
#define GETBITBYTE(x,i) ( (((char)(x)) >> (i)) & 0x01 )
@@ -91,8 +90,8 @@ typedef char *BITVECP;
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITBYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 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 ARRKEY 0x01
#define SIGNKEY 0x02
@@ -102,7 +101,7 @@ typedef char *BITVECP;
#define ISSIGNKEY(x) ( ((TRGM*)x)->flag & SIGNKEY )
#define ISALLTRUE(x) ( ((TRGM*)x)->flag & ALLISTRUE )
-#define CALCGTSIZE(flag, len) ( TRGMHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(trgm)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( TRGMHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(trgm)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
#define GETSIGN(x) ( (BITVECP)( (char*)x+TRGMHDRSIZE ) )
#define GETARR(x) ( (trgm*)( (char*)x+TRGMHDRSIZE ) )
#define ARRNELEM(x) ( ( VARSIZE(x) - TRGMHDRSIZE )/sizeof(trgm) )
diff --git a/contrib/pg_trgm/trgm_gist.c b/contrib/pg_trgm/trgm_gist.c
index f1e0547..9f653ec 100644
--- a/contrib/pg_trgm/trgm_gist.c
+++ b/contrib/pg_trgm/trgm_gist.c
@@ -5,9 +5,16 @@
#include "trgm.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "fmgr.h"
+/* gist_trgm_ops opclass options */
+typedef struct TrgmGistOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} TrgmGistOptions;
typedef struct
{
@@ -38,6 +45,7 @@ PG_FUNCTION_INFO_V1(gtrgm_union);
PG_FUNCTION_INFO_V1(gtrgm_same);
PG_FUNCTION_INFO_V1(gtrgm_penalty);
PG_FUNCTION_INFO_V1(gtrgm_picksplit);
+PG_FUNCTION_INFO_V1(gtrgm_options);
/* Number of one-bits in an unsigned byte */
static const uint8 number_of_ones[256] = {
@@ -74,20 +82,41 @@ gtrgm_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+static TRGM *
+gtrgm_alloc(bool isalltrue, int siglen, BITVECP sign)
+{
+ int flag = SIGNKEY | (isalltrue ? ALLISTRUE : 0);
+ int size = CALCGTSIZE(flag, siglen);
+ TRGM *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if (!isalltrue)
+ {
+ if (sign)
+ memcpy(GETSIGN(res), sign, siglen);
+ else
+ memset(GETSIGN(res), 0, siglen);
+ }
+
+ return res;
+}
+
static void
-makesign(BITVECP sign, TRGM *a)
+makesign(BITVECP sign, TRGM *a, int siglen)
{
int32 k,
len = ARRNELEM(a);
trgm *ptr = GETARR(a);
int32 tmp = 0;
- MemSet((void *) sign, 0, sizeof(BITVEC));
- SETBIT(sign, SIGLENBIT); /* set last unused bit */
+ MemSet((void *) sign, 0, siglen);
+ SETBIT(sign, SIGLENBIT(siglen)); /* set last unused bit */
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
- HASH(sign, tmp);
+ HASH(sign, tmp, siglen);
}
}
@@ -95,6 +124,8 @@ Datum
gtrgm_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(1);
+ int siglen = options->siglen;
GISTENTRY *retval = entry;
if (entry->leafkey)
@@ -111,22 +142,17 @@ gtrgm_compress(PG_FUNCTION_ARGS)
else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
!ISALLTRUE(DatumGetPointer(entry->key)))
{
- int32 i,
- len;
+ int32 i;
TRGM *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 = (TRGM *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = SIGNKEY | ALLISTRUE;
-
+ res = gtrgm_alloc(true, siglen, sign);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
entry->rel, entry->page,
@@ -160,7 +186,7 @@ gtrgm_decompress(PG_FUNCTION_ARGS)
}
static int32
-cnt_sml_sign_common(TRGM *qtrg, BITVECP sign)
+cnt_sml_sign_common(TRGM *qtrg, BITVECP sign, int siglen)
{
int32 count = 0;
int32 k,
@@ -171,7 +197,7 @@ cnt_sml_sign_common(TRGM *qtrg, BITVECP sign)
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
- count += GETBIT(sign, HASHVAL(tmp));
+ count += GETBIT(sign, HASHVAL(tmp, siglen));
}
return count;
@@ -186,6 +212,8 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
TRGM *key = (TRGM *) DatumGetPointer(entry->key);
TRGM *qtrg;
bool res;
@@ -313,7 +341,7 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
}
else
{ /* non-leaf contains signature */
- int32 count = cnt_sml_sign_common(qtrg, GETSIGN(key));
+ int32 count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
int32 len = ARRNELEM(qtrg);
if (len == 0)
@@ -355,7 +383,7 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
- if (!GETBIT(sign, HASHVAL(tmp)))
+ if (!GETBIT(sign, HASHVAL(tmp, siglen)))
{
res = false;
break;
@@ -408,7 +436,7 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
- check[k] = GETBIT(sign, HASHVAL(tmp));
+ check[k] = GETBIT(sign, HASHVAL(tmp, siglen));
}
res = trigramsMatchGraph(cache->graph, check);
pfree(check);
@@ -438,6 +466,8 @@ gtrgm_distance(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
TRGM *key = (TRGM *) DatumGetPointer(entry->key);
TRGM *qtrg;
float8 res;
@@ -495,7 +525,7 @@ gtrgm_distance(PG_FUNCTION_ARGS)
}
else
{ /* non-leaf contains signature */
- int32 count = cnt_sml_sign_common(qtrg, GETSIGN(key));
+ int32 count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
int32 len = ARRNELEM(qtrg);
res = (len == 0) ? -1.0 : 1.0 - ((float8) count) / ((float8) len);
@@ -511,7 +541,7 @@ gtrgm_distance(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, TRGM *add)
+unionkey(BITVECP sbase, TRGM *add, int siglen)
{
int32 i;
@@ -522,7 +552,7 @@ unionkey(BITVECP sbase, TRGM *add)
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
@@ -533,7 +563,7 @@ unionkey(BITVECP sbase, TRGM *add)
for (i = 0; i < ARRNELEM(add); i++)
{
CPTRGM(((char *) &tmp), ptr + i);
- HASH(sbase, tmp);
+ HASH(sbase, tmp, siglen);
}
}
return 0;
@@ -546,29 +576,23 @@ gtrgm_union(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int32 len = entryvec->n;
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
int32 i;
- int32 flag = 0;
- TRGM *result;
+ TRGM *result = gtrgm_alloc(false, siglen, NULL);
+ BITVECP base = GETSIGN(result);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < len; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag = ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(ALLISTRUE, siglen));
break;
}
}
- flag |= SIGNKEY;
- len = CALCGTSIZE(flag, 0);
- result = (TRGM *) palloc(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -579,6 +603,8 @@ gtrgm_same(PG_FUNCTION_ARGS)
TRGM *a = (TRGM *) PG_GETARG_POINTER(0);
TRGM *b = (TRGM *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
if (ISSIGNKEY(a))
{ /* then b also ISSIGNKEY */
@@ -595,7 +621,7 @@ gtrgm_same(PG_FUNCTION_ARGS)
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -632,24 +658,24 @@ gtrgm_same(PG_FUNCTION_ARGS)
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
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]);
dist += number_of_ones[diff];
@@ -658,19 +684,19 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(TRGM *a, TRGM *b)
+hemdist(TRGM *a, TRGM *b, int siglen)
{
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(b), siglen);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(a), siglen);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglen);
}
Datum
@@ -679,6 +705,8 @@ gtrgm_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);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
TRGM *origval = (TRGM *) DatumGetPointer(origentry->key);
TRGM *newval = (TRGM *) DatumGetPointer(newentry->key);
BITVECP orig = GETSIGN(origval);
@@ -688,7 +716,7 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
if (ISARRKEY(newval))
{
char *cache = (char *) fcinfo->flinfo->fn_extra;
- TRGM *cachedVal = (TRGM *) (cache + MAXALIGN(sizeof(BITVEC)));
+ TRGM *cachedVal = (TRGM *) (cache + MAXALIGN(siglen));
Size newvalsize = VARSIZE(newval);
BITVECP sign;
@@ -702,12 +730,12 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
char *newcache;
newcache = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- MAXALIGN(sizeof(BITVEC)) +
+ MAXALIGN(siglen) +
newvalsize);
- makesign((BITVECP) newcache, newval);
+ makesign((BITVECP) newcache, newval, siglen);
- cachedVal = (TRGM *) (newcache + MAXALIGN(sizeof(BITVEC)));
+ cachedVal = (TRGM *) (newcache + MAXALIGN(siglen));
memcpy(cachedVal, newval, newvalsize);
if (cache)
@@ -719,31 +747,32 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
sign = (BITVECP) cache;
if (ISALLTRUE(origval))
- *penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+ *penalty = ((float) (SIGLENBIT(siglen) - sizebitvec(sign, siglen))) / (float) (SIGLENBIT(siglen) + 1);
else
- *penalty = hemdistsign(sign, orig);
+ *penalty = hemdistsign(sign, orig, siglen);
}
else
- *penalty = hemdist(origval, newval);
+ *penalty = hemdist(origval, newval, siglen);
PG_RETURN_POINTER(penalty);
}
typedef struct
{
bool allistrue;
- BITVEC sign;
+ BITVECP sign;
} CACHESIGN;
static void
-fillcache(CACHESIGN *item, TRGM *key)
+fillcache(CACHESIGN *item, TRGM *key, BITVECP sign, int siglen)
{
item->allistrue = false;
+ item->sign = sign;
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) )
@@ -764,19 +793,19 @@ comparecost(const void *a, const void *b)
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
@@ -785,6 +814,8 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
OffsetNumber maxoff = entryvec->n - 2;
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber k,
j;
TRGM *datum_l,
@@ -803,19 +834,23 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
BITVECP ptr;
int i;
CACHESIGN *cache;
+ char *cache_sign;
SPLITCOST *costvector;
/* cache the sign data for each existing item */
cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
+ cache_sign = palloc(siglen * (maxoff + 2));
+
for (k = FirstOffsetNumber; k <= maxoff; k = OffsetNumberNext(k))
- fillcache(&cache[k], GETENTRY(entryvec, k));
+ fillcache(&cache[k], GETENTRY(entryvec, k), &cache_sign[siglen * k],
+ siglen);
/* now find the two furthest-apart items */
for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
{
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
- size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+ size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -840,44 +875,22 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
v->spl_nright = 0;
/* form initial .. */
- if (cache[seed_1].allistrue)
- {
- datum_l = (TRGM *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_l->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_l = (TRGM *) 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 = (TRGM *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_r->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_r = (TRGM *) 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 = gtrgm_alloc(cache[seed_1].allistrue, siglen, cache[seed_1].sign);
+ datum_r = gtrgm_alloc(cache[seed_2].allistrue, 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),
+ &cache_sign[siglen * 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);
@@ -903,36 +916,36 @@ gtrgm_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;
@@ -943,12 +956,12 @@ gtrgm_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;
@@ -962,3 +975,20 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(v);
}
+
+Datum
+gtrgm_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(TrgmGistOptions, siglen) };
+ TrgmGistOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(TrgmGistOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/pgtrgm.sgml b/doc/src/sgml/pgtrgm.sgml
index 83b0033..a4151aa 100644
--- a/doc/src/sgml/pgtrgm.sgml
+++ b/doc/src/sgml/pgtrgm.sgml
@@ -368,6 +368,15 @@ CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);
</para>
<para>
+ GiST opclass <literal>gist_trgm_ops</literal> has one optional parameter
+ <literal>siglen</literal> for specifying signature length in bytes
+ (default signature length is 12 bytes, maximal length is 488 bytes):
+<programlisting>
+CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops(siglen=32));
+</programlisting>
+ </para>
+
+ <para>
At this point, you will have an index on the <structfield>t</structfield> column that
you can use for similarity searching. A typical query is
<programlisting>
--
2.7.4
[text/x-patch] 0008-Add-opclass-parameters-to-contrib-hstore-v02.patch (17.6K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/9-0008-Add-opclass-parameters-to-contrib-hstore-v02.patch)
download | inline diff:
From 413cd728ba7db42f23315b08ee00c163d3f1c7eb Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 9 Feb 2018 18:34:29 +0300
Subject: [PATCH 8/9] Add opclass parameters to contrib/hstore
---
contrib/hstore/Makefile | 2 +-
contrib/hstore/hstore--1.5--1.6.sql | 32 ++++++
contrib/hstore/hstore.control | 2 +-
contrib/hstore/hstore_gist.c | 200 ++++++++++++++++++++----------------
doc/src/sgml/hstore.sgml | 9 ++
5 files changed, 154 insertions(+), 91 deletions(-)
create mode 100644 contrib/hstore/hstore--1.5--1.6.sql
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index 46d26f8..a8a5105 100644
--- a/contrib/hstore/Makefile
+++ b/contrib/hstore/Makefile
@@ -5,7 +5,7 @@ OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
$(WIN32RES)
EXTENSION = hstore
-DATA = hstore--1.4.sql hstore--1.4--1.5.sql \
+DATA = hstore--1.4.sql hstore--1.4--1.5.sql hstore--1.5--1.6.sql \
hstore--1.3--1.4.sql hstore--1.2--1.3.sql \
hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
hstore--unpackaged--1.0.sql
diff --git a/contrib/hstore/hstore--1.5--1.6.sql b/contrib/hstore/hstore--1.5--1.6.sql
new file mode 100644
index 0000000..08c85ee
--- /dev/null
+++ b/contrib/hstore/hstore--1.5--1.6.sql
@@ -0,0 +1,32 @@
+/* contrib/hstore/hstore--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION intarray UPDATE TO '1.6'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('ghstore_compress(internal)', '{internal,internal}'),
+('ghstore_decompress(internal)', '{internal,internal}'),
+('ghstore_consistent(internal,hstore,smallint,oid,internal)', '{internal,hstore,smallint,oid,internal,internal}'),
+('ghstore_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('ghstore_picksplit(internal,internal)', '{internal,internal,internal}'),
+('ghstore_union(internal,internal)', '{internal,internal,internal}'),
+('ghstore_same(ghstore,ghstore,internal)', '{ghstore,ghstore,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION ghstore_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'ghstore_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_hstore_ops USING gist
+ADD FUNCTION 10 (hstore) ghstore_options (internal, boolean);
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control
index 8a71947..93688cd 100644
--- a/contrib/hstore/hstore.control
+++ b/contrib/hstore/hstore.control
@@ -1,5 +1,5 @@
# hstore extension
comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.5'
+default_version = '1.6'
module_pathname = '$libdir/hstore'
relocatable = true
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index 6d24d2f..13be2c0 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -4,29 +4,33 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
#include "utils/pg_crc.h"
#include "hstore.h"
+/* gist_hstore_ops opclass options */
+typedef struct HstoreGistOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} GistHstoreOptions;
+
/* bigint defines */
#define BITBYTE 8
-#define SIGLENINT 4 /* >122 => key will toast, so very slow!!! */
-#define SIGLEN ( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
+#define SIGLEN_DEFAULT (sizeof(int32) * 4)
+#define SIGLEN_MAX (sizeof(int32) * 122) /* key will toast, so very slow!!! */
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define SIGPTR(x) ( (BITVECP) ARR_DATA_PTR(x) )
-
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < (siglen); i++)
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
-
-#define LOOPBIT \
- for(i=0;i<SIGLENBIT;i++)
+#define LOOPBIT(siglen) \
+ for (i = 0; i < SIGLENBIT(siglen); i++)
/* beware of multiple evaluation of arguments to these macros! */
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
@@ -34,8 +38,8 @@ typedef char *BITVECP;
#define CLRBIT(x,i) GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITBYTE ) )
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITBYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 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))
typedef struct
{
@@ -49,7 +53,7 @@ typedef struct
#define ISALLTRUE(x) ( ((GISTTYPE*)x)->flag & ALLISTRUE )
#define GTHDRSIZE (VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
#define GETSIGN(x) ( (BITVECP)( (char*)x+GTHDRSIZE ) )
@@ -100,6 +104,27 @@ ghstore_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+static GISTTYPE *
+ghstore_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+ int flag = allistrue ? ALLISTRUE : 0;
+ int size = CALCGTSIZE(flag, siglen);
+ GISTTYPE *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if (!allistrue)
+ {
+ if (sign)
+ memcpy(GETSIGN(res), sign, siglen);
+ else
+ memset(GETSIGN(res), 0, siglen);
+ }
+
+ return res;
+}
+
PG_FUNCTION_INFO_V1(ghstore_consistent);
PG_FUNCTION_INFO_V1(ghstore_compress);
PG_FUNCTION_INFO_V1(ghstore_decompress);
@@ -107,36 +132,37 @@ PG_FUNCTION_INFO_V1(ghstore_penalty);
PG_FUNCTION_INFO_V1(ghstore_picksplit);
PG_FUNCTION_INFO_V1(ghstore_union);
PG_FUNCTION_INFO_V1(ghstore_same);
+PG_FUNCTION_INFO_V1(ghstore_options);
Datum
ghstore_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(1);
+ int siglen = options->siglen;
GISTENTRY *retval = entry;
if (entry->leafkey)
{
- GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+ GISTTYPE *res = ghstore_alloc(false, siglen, NULL);
HStore *val = DatumGetHStoreP(entry->key);
HEntry *hsent = ARRPTR(val);
char *ptr = STRPTR(val);
int count = HS_COUNT(val);
int i;
- SET_VARSIZE(res, CALCGTSIZE(0));
-
for (i = 0; i < count; ++i)
{
int h;
h = crc32_sz((char *) HSTORE_KEY(hsent, ptr, i),
HSTORE_KEYLEN(hsent, i));
- HASH(GETSIGN(res), h);
+ HASH(GETSIGN(res), h, siglen);
if (!HSTORE_VALISNULL(hsent, i))
{
h = crc32_sz((char *) HSTORE_VAL(hsent, ptr, i),
HSTORE_VALLEN(hsent, i));
- HASH(GETSIGN(res), h);
+ HASH(GETSIGN(res), h, siglen);
}
}
@@ -152,15 +178,13 @@ ghstore_compress(PG_FUNCTION_ARGS)
GISTTYPE *res;
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
- SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
- res->flag = ALLISTRUE;
+ res = ghstore_alloc(true, siglen, NULL);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
@@ -188,6 +212,9 @@ ghstore_same(PG_FUNCTION_ARGS)
GISTTYPE *a = (GISTTYPE *) PG_GETARG_POINTER(0);
GISTTYPE *b = (GISTTYPE *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
+
if (ISALLTRUE(a) && ISALLTRUE(b))
*result = true;
@@ -202,7 +229,7 @@ ghstore_same(PG_FUNCTION_ARGS)
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -215,12 +242,12 @@ ghstore_same(PG_FUNCTION_ARGS)
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
size += SUMBIT(sign);
sign = (BITVECP) (((char *) sign) + 1);
@@ -229,12 +256,12 @@ sizebitvec(BITVECP sign)
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
dist = 0;
- LOOPBIT
+ LOOPBIT(siglen)
{
if (GETBIT(a, i) != GETBIT(b, i))
dist++;
@@ -243,30 +270,30 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *b, int siglen)
{
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(b), siglen);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(a), siglen);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglen);
}
static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
{
int32 i;
BITVECP sadd = GETSIGN(add);
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
return 0;
}
@@ -278,28 +305,23 @@ ghstore_union(PG_FUNCTION_ARGS)
int32 len = entryvec->n;
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
int32 i;
- int32 flag = 0;
- GISTTYPE *result;
+ GISTTYPE *result = ghstore_alloc(false, siglen, NULL);
+ BITVECP base = GETSIGN(result);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < len; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag |= ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(ALLISTRUE, siglen));
break;
}
}
- len = CALCGTSIZE(flag);
- result = (GISTTYPE *) palloc(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -310,10 +332,12 @@ ghstore_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);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
GISTTYPE *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
GISTTYPE *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
- *penalty = hemdist(origval, newval);
+ *penalty = hemdist(origval, newval, siglen);
PG_RETURN_POINTER(penalty);
}
@@ -338,6 +362,8 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
OffsetNumber maxoff = entryvec->n - 2;
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber k,
j;
GISTTYPE *datum_l,
@@ -368,7 +394,7 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
_k = GETENTRY(entryvec, k);
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
- size_waste = hemdist(_k, GETENTRY(entryvec, j));
+ size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -390,33 +416,10 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_l, GTHDRSIZE);
- datum_l->flag = ALLISTRUE;
- }
- else
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
- datum_l->flag = 0;
- memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC))
- ;
- }
- if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_r, GTHDRSIZE);
- datum_r->flag = ALLISTRUE;
- }
- else
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
- datum_r->flag = 0;
- memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
- }
+ datum_l = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_1)));
+ datum_r = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_2)));
maxoff = OffsetNumberNext(maxoff);
/* sort before ... */
@@ -425,8 +428,8 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
{
costvector[j - 1].pos = j;
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
costvector[j - 1].cost = abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -450,20 +453,20 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
continue;
}
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.0001))
{
if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_l, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -474,12 +477,12 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_r))
- MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_r, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -504,6 +507,8 @@ ghstore_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
bool res = true;
BITVECP sign;
@@ -529,13 +534,13 @@ ghstore_consistent(PG_FUNCTION_ARGS)
int crc = crc32_sz((char *) HSTORE_KEY(qe, qv, i),
HSTORE_KEYLEN(qe, i));
- if (GETBIT(sign, HASHVAL(crc)))
+ if (GETBIT(sign, HASHVAL(crc, siglen)))
{
if (!HSTORE_VALISNULL(qe, i))
{
crc = crc32_sz((char *) HSTORE_VAL(qe, qv, i),
HSTORE_VALLEN(qe, i));
- if (!GETBIT(sign, HASHVAL(crc)))
+ if (!GETBIT(sign, HASHVAL(crc, siglen)))
res = false;
}
}
@@ -548,7 +553,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
text *query = PG_GETARG_TEXT_PP(1);
int crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
- res = (GETBIT(sign, HASHVAL(crc))) ? true : false;
+ res = (GETBIT(sign, HASHVAL(crc, siglen))) ? true : false;
}
else if (strategy == HStoreExistsAllStrategyNumber)
{
@@ -569,7 +574,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
if (key_nulls[i])
continue;
crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
- if (!(GETBIT(sign, HASHVAL(crc))))
+ if (!(GETBIT(sign, HASHVAL(crc, siglen))))
res = false;
}
}
@@ -594,7 +599,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
if (key_nulls[i])
continue;
crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
- if (GETBIT(sign, HASHVAL(crc)))
+ if (GETBIT(sign, HASHVAL(crc, siglen)))
res = true;
}
}
@@ -603,3 +608,20 @@ ghstore_consistent(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(res);
}
+
+Datum
+ghstore_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(GistHstoreOptions, siglen) };
+ GistHstoreOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(GistHstoreOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index 94ccd12..45a811f 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -462,6 +462,15 @@ CREATE INDEX hidx ON testhstore USING GIN (h);
</programlisting>
<para>
+ GiST opclass <literal>gist_hstore_ops</literal> has one optional parameter
+ <literal>siglen</literal> for specifying signature length in bytes
+ (default signature length is 16 bytes, maximal length is 488 bytes):
+<programlisting>
+ CREATE INDEX hidx ON testhstore USING GIST (h gist_hstore_ops(siglen=32));
+</programlisting>
+ </para>
+
+ <para>
<type>hstore</type> also supports <type>btree</type> or <type>hash</type> indexes for
the <literal>=</literal> operator. This allows <type>hstore</type> columns to be
declared <literal>UNIQUE</literal>, or to be used in <literal>GROUP BY</literal>,
--
2.7.4
view thread (34+ messages) latest in thread
Message-ID: <71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru>
Permalink: ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/
Also on: postgresql.org/message-id/71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@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, dhyan@nataraj.su, obartunov@gmail.com
Subject: Re: [PATCH] Opclass parameters
In-Reply-To: <71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@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