Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1gNJbZ-0006rp-42 for pgsql-hackers@arkaria.postgresql.org; Thu, 15 Nov 2018 15:30:54 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1gNJbX-00024i-1B for pgsql-hackers@arkaria.postgresql.org; Thu, 15 Nov 2018 15:30:51 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1gNJbV-00024X-UP for pgsql-hackers@lists.postgresql.org; Thu, 15 Nov 2018 15:30:50 +0000 Received: from mail.postgrespro.ru ([93.174.131.138]) by makus.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1gNJbC-000591-3G for pgsql-hackers@postgresql.org; Thu, 15 Nov 2018 15:30:48 +0000 Received: from localhost (localhost [127.0.0.1]) by mail.postgrespro.ru (Postfix) with ESMTP id 6B74421C7902; Thu, 15 Nov 2018 18:30:27 +0300 (MSK) X-Virus-Scanned: Debian amavisd-new at postgrespro.ru X-Spam-Flag: NO X-Spam-Score: 0 X-Spam-Level: X-Spam-Status: No, score=x tagged_above=-99 required=4 WHITELISTED tests=[] autolearn=unavailable Received: from [10.100.236.31] (unknown [195.82.154.27]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client did not present a certificate) by mail.postgrespro.ru (Postfix) with ESMTPSA id F05C321C787E; Thu, 15 Nov 2018 18:30:26 +0300 (MSK) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=postgrespro.ru; s=mail; t=1542295827; bh=6ZnY7sg0Ug0rpIxjSETdOS5Sg4y58oIAXkG7SGFqYcs=; h=Subject:From:To:Cc:References:Date:In-Reply-To; b=apwQ6TgE4pSe5OuzEFiqCz3xAKrRxqzv9YM4hwTjOv2/9Lus/G3hmR03JlOfOc1RQ +HmJ1kQkJX5n/frpfkkGM2KUlaTps5pQIRm8VpZWaNfifCuc50UHBbM2zVyAelNanV MWoXdj/vhYSEf+mfy+/5P7c51MhXJSAqauHxPuz8= Subject: Re: [PATCH] Opclass parameters From: Nikita Glukhov To: PostgreSQL mailing lists Cc: Nikolay Shaplov , obartunov@gmail.com References: <15077864.qMPJiTemUy@x200m> <2803786.3sFNWAn8Pn@x200m> <6382cb29-83d8-90f2-1dbb-b4ac606cbf1f@postgrespro.ru> Organization: Postgres Professional Message-ID: <71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru> Date: Thu, 15 Nov 2018 18:26:43 +0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.2.1 MIME-Version: 1.0 In-Reply-To: <6382cb29-83d8-90f2-1dbb-b4ac606cbf1f@postgrespro.ru> Content-Type: multipart/mixed; boundary="------------DE4F188FEBE588AD0866D67F" Content-Language: en-US List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk This is a multi-part message in MIME format. --------------DE4F188FEBE588AD0866D67F Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit 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 --------------DE4F188FEBE588AD0866D67F Content-Type: text/x-patch; name="0001-Add-opclass-parameters-v02.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0001-Add-opclass-parameters-v02.patch" From 76513025cf34b2a72c8396a3d868db286fe54dbb Mon Sep 17 00:00:00 2001 From: Nikita Glukhov 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$<iteration count>:&l partial index. + + + indoptions + text[] + + + This is an array of indnatts 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. + + 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 operator class for each column of an index. -CREATE INDEX name ON table (column opclass sort options , ...); +CREATE INDEX name ON table (column opclass [ ( opclass_options ) ] sort options , ...); The operator class identifies the operators to be used by the index for that column. For example, a B-tree index on the type int4 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 CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] name ] ON [ ONLY ] table_name [ USING method ] - ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ) + ( { column_name | ( expression ) } [ COLLATE collation ] { opclass | DEFAULT } [ ( opclass_parameter = value [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ) [ INCLUDE ( column_name [, ...] ) ] [ WITH ( storage_parameter = value [, ... ] ) ] [ TABLESPACE tablespace_name ] @@ -279,6 +279,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] + opclass_parameter + + + The name of an operator class parameter. See below for details. + + + + + ASC @@ -607,8 +616,9 @@ Indexes: - An operator class can be specified for each - column of an index. The operator class identifies the operators to be + An operator class 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 int4_ops 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_clause opt_alias_clause %type func_alias_clause %type sortby -%type index_elem +%type index_elem index_elem_options %type table_ref %type joined_table %type 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 --------------DE4F188FEBE588AD0866D67F Content-Type: text/x-patch; name="0002-Add-opclass-parameters-to-GiST-v02.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0002-Add-opclass-parameters-to-GiST-v02.patch" From 8618654d93554f0684b0cf3c89de049d6d29435c Mon Sep 17 00:00:00 2001 From: Nikita Glukhov 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) 9 + + options + parse opclass-specific options (optional) + 10 + 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 --------------DE4F188FEBE588AD0866D67F Content-Type: text/x-patch; name="0003-Add-opclass-parameters-to-GIN-v02.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Add-opclass-parameters-to-GIN-v02.patch" From 8a015b2803852a03da2d1bde42951473cca1d3c7 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov 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 @@ 6 + + options + + parse opclass-specific options (optional) + + 7 + 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 --------------DE4F188FEBE588AD0866D67F Content-Type: text/x-patch; name="0004-Add-opclass-parameters-to-GiST-tsvector_ops-v02.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0004-Add-opclass-parameters-to-GiST-tsvector_ops-v02.patch" From af502308192d3f316e493682a36f63733881043b Mon Sep 17 00:00:00 2001 From: Nikita Glukhov 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'); text search - CREATE INDEX name ON table USING GIST (column); + CREATE INDEX name ON table USING GIST (column [ { DEFAULT | tsvector_ops } (siglen = number) ] ); @@ -3643,6 +3643,8 @@ SELECT plainto_tsquery('supernovae stars'); Creates a GiST (Generalized Search Tree)-based index. The column can be of tsvector or tsquery type. + Optional integer parameter siglen determines + signature length in bytes (see below for details). @@ -3666,7 +3668,10 @@ SELECT plainto_tsquery('supernovae stars'); to check the actual table row to eliminate such false matches. (PostgreSQL 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 siglen. + Default signature length (when siglen 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> (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 --------------DE4F188FEBE588AD0866D67F Content-Type: text/x-patch; name="0005-Add-opclass-parameters-to-contrib-intarray-v02.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0005-Add-opclass-parameters-to-contrib-intarray-v02.patch" From c246a65e9a613f57e8812d32c395705bb940cfef Mon Sep 17 00:00:00 2001 From: Nikita Glukhov 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> ( (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 #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 @@ - Two GiST index operator classes are provided: - gist__int_ops (used by default) is suitable for + Two parametrized GiST index operator classes are provided: + gist__int_ops(numranges) (used by default) is suitable for small- to medium-size data sets, while - gist__intbig_ops uses a larger signature and is more + gist__intbig_ops(siglen) 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 numranges of + gist__int_ops determines + number of ranges in compressed array (100 by default). + + Optional integer parameter siglen of + gist__intbig_ops determines + signature length in bytes (252 by default, 488 maximum). -- 2.7.4 --------------DE4F188FEBE588AD0866D67F Content-Type: text/x-patch; name="0006-Add-opclass-parameters-to-contrib-ltree-v02.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0006-Add-opclass-parameters-to-contrib-ltree-v02.patch" From 5a69a859c8d066ae73394e67a13b9f535534c1dd Mon Sep 17 00:00:00 2001 From: Nikita Glukhov 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> 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;inumlevel == (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 @, ~, ? - Example of creating such an index: + Example of creating such an index with a default signature length of 8 bytes: CREATE INDEX path_gist_idx ON test USING GIST (path); + + Example of creating such an index with a signature length of 100 bytes: + + +CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100)); + @@ -513,12 +519,18 @@ CREATE INDEX path_gist_idx ON test USING GIST (path); @, ~, ? - Example of creating such an index: + Example of creating such an index with a default signature length of 28 bytes: CREATE INDEX path_gist_idx ON test USING GIST (array_path); + Example of creating such an index with a signature length of 100 bytes: + + +CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100)); + + Note: This index type is lossy. -- 2.7.4 --------------DE4F188FEBE588AD0866D67F Content-Type: text/x-patch; name="0007-Add-opclass-parameters-to-contrib-pg_trgm-v02.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0007-Add-opclass-parameters-to-contrib-pg_trgm-v02.patch" From 585da1d8f6dd8f72a3b5f3d8d6ff0cdb4db90655 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov 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> (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); + GiST opclass gist_trgm_ops has one optional parameter + siglen for specifying signature length in bytes + (default signature length is 12 bytes, maximal length is 488 bytes): + +CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops(siglen=32)); + + + + At this point, you will have an index on the t column that you can use for similarity searching. A typical query is -- 2.7.4 --------------DE4F188FEBE588AD0866D67F Content-Type: text/x-patch; name="0008-Add-opclass-parameters-to-contrib-hstore-v02.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0008-Add-opclass-parameters-to-contrib-hstore-v02.patch" From 413cd728ba7db42f23315b08ee00c163d3f1c7eb Mon Sep 17 00:00:00 2001 From: Nikita Glukhov 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> ( (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); + GiST opclass gist_hstore_ops has one optional parameter + siglen for specifying signature length in bytes + (default signature length is 16 bytes, maximal length is 488 bytes): + + CREATE INDEX hidx ON testhstore USING GIST (h gist_hstore_ops(siglen=32)); + + + + hstore also supports btree or hash indexes for the = operator. This allows hstore columns to be declared UNIQUE, or to be used in GROUP BY, -- 2.7.4 --------------DE4F188FEBE588AD0866D67F--