diff options
Diffstat (limited to 'src/backend')
33 files changed, 1185 insertions, 316 deletions
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index c481838389c..7db3ae5ee0c 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -90,6 +90,7 @@ brinhandler(PG_FUNCTION_ARGS) amroutine->amstrategies = 0; amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM; + amroutine->amoptsprocnum = BRIN_PROCNUM_OPTIONS; amroutine->amcanorder = false; amroutine->amcanorderbyop = false; amroutine->amcanbackward = false; diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index 1302ebb6681..fb0615463e0 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -105,6 +105,9 @@ brinvalidate(Oid opclassoid) 3, 3, INTERNALOID, INTERNALOID, INTERNALOID); break; + case BRIN_PROCNUM_OPTIONS: + ok = check_amoptsproc_signature(procform->amproc); + break; default: /* Complain if it's not a valid optional proc number */ if (procform->amprocnum < BRIN_FIRST_OPTIONAL_PROCNUM || diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index e136116d7ba..8ccc228a8cc 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -702,6 +702,47 @@ add_reloption(relopt_gen *newoption) } /* + * init_local_reloptions + * Initialize local reloptions that will parsed into bytea structure of + * 'relopt_struct_size'. + */ +void +init_local_reloptions(local_relopts *opts, Size relopt_struct_size) +{ + opts->options = NIL; + opts->validators = NIL; + opts->relopt_struct_size = relopt_struct_size; +} + +/* + * register_reloptions_validator + * Register custom validation callback that will be called at the end of + * build_local_reloptions(). + */ +void +register_reloptions_validator(local_relopts *opts, relopts_validator validator) +{ + opts->validators = lappend(opts->validators, validator); +} + +/* + * add_local_reloption + * Add an already-created custom reloption to the local list. + */ +static void +add_local_reloption(local_relopts *relopts, relopt_gen *newoption, int offset) +{ + local_relopt *opt = palloc(sizeof(*opt)); + + Assert(offset < relopts->relopt_struct_size); + + opt->option = newoption; + opt->offset = offset; + + relopts->options = lappend(relopts->options, opt); +} + +/* * allocate_reloption * Allocate a new reloption and initialize the type-agnostic fields * (for types other than string) @@ -714,7 +755,10 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc, size_t size; relopt_gen *newoption; - oldcxt = MemoryContextSwitchTo(TopMemoryContext); + if (kinds != RELOPT_KIND_LOCAL) + oldcxt = MemoryContextSwitchTo(TopMemoryContext); + else + oldcxt = NULL; switch (type) { @@ -750,18 +794,19 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc, newoption->type = type; newoption->lockmode = lockmode; - MemoryContextSwitchTo(oldcxt); + if (oldcxt != NULL) + MemoryContextSwitchTo(oldcxt); return newoption; } /* - * add_bool_reloption - * Add a new boolean reloption + * init_bool_reloption + * Allocate and initialize a new boolean reloption */ -void -add_bool_reloption(bits32 kinds, const char *name, const char *desc, - bool default_val, LOCKMODE lockmode) +static relopt_bool * +init_bool_reloption(bits32 kinds, const char *name, const char *desc, + bool default_val, LOCKMODE lockmode) { relopt_bool *newoption; @@ -769,16 +814,49 @@ add_bool_reloption(bits32 kinds, const char *name, const char *desc, name, desc, lockmode); newoption->default_val = default_val; + return newoption; +} + +/* + * add_bool_reloption + * Add a new boolean reloption + */ +void +add_bool_reloption(bits32 kinds, const char *name, const char *desc, + bool default_val, LOCKMODE lockmode) +{ + relopt_bool *newoption = init_bool_reloption(kinds, name, desc, + default_val, lockmode); + add_reloption((relopt_gen *) newoption); } /* - * add_int_reloption - * Add a new integer reloption + * add_local_bool_reloption + * Add a new boolean local reloption + * + * 'offset' is offset of bool-typed field. */ void -add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val, - int min_val, int max_val, LOCKMODE lockmode) +add_local_bool_reloption(local_relopts *relopts, const char *name, + const char *desc, bool default_val, int offset) +{ + relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL, + name, desc, + default_val, 0); + + add_local_reloption(relopts, (relopt_gen *) newoption, offset); +} + + +/* + * init_real_reloption + * Allocate and initialize a new integer reloption + */ +static relopt_int * +init_int_reloption(bits32 kinds, const char *name, const char *desc, + int default_val, int min_val, int max_val, + LOCKMODE lockmode) { relopt_int *newoption; @@ -788,16 +866,50 @@ add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_ newoption->min = min_val; newoption->max = max_val; + return newoption; +} + +/* + * add_int_reloption + * Add a new integer reloption + */ +void +add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val, + int min_val, int max_val, LOCKMODE lockmode) +{ + relopt_int *newoption = init_int_reloption(kinds, name, desc, + default_val, min_val, + max_val, lockmode); + add_reloption((relopt_gen *) newoption); } /* - * add_real_reloption - * Add a new float reloption + * add_local_int_reloption + * Add a new local integer reloption + * + * 'offset' is offset of int-typed field. */ void -add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val, - double min_val, double max_val, LOCKMODE lockmode) +add_local_int_reloption(local_relopts *relopts, const char *name, + const char *desc, int default_val, int min_val, + int max_val, int offset) +{ + relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL, + name, desc, default_val, + min_val, max_val, 0); + + add_local_reloption(relopts, (relopt_gen *) newoption, offset); +} + +/* + * init_real_reloption + * Allocate and initialize a new real reloption + */ +static relopt_real * +init_real_reloption(bits32 kinds, const char *name, const char *desc, + double default_val, double min_val, double max_val, + LOCKMODE lockmode) { relopt_real *newoption; @@ -807,10 +919,66 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc, double defa newoption->min = min_val; newoption->max = max_val; + return newoption; +} + +/* + * add_real_reloption + * Add a new float reloption + */ +void +add_real_reloption(bits32 kinds, const char *name, const char *desc, + double default_val, double min_val, double max_val, + LOCKMODE lockmode) +{ + relopt_real *newoption = init_real_reloption(kinds, name, desc, + default_val, min_val, + max_val, lockmode); + add_reloption((relopt_gen *) newoption); } /* + * add_local_real_reloption + * Add a new local float reloption + * + * 'offset' is offset of double-typed field. + */ +void +add_local_real_reloption(local_relopts *relopts, const char *name, + const char *desc, double default_val, + double min_val, double max_val, int offset) +{ + relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL, + name, desc, + default_val, min_val, + max_val, 0); + + add_local_reloption(relopts, (relopt_gen *) newoption, offset); +} + +/* + * init_enum_reloption + * Allocate and initialize a new enum reloption + */ +static relopt_enum * +init_enum_reloption(bits32 kinds, const char *name, const char *desc, + relopt_enum_elt_def *members, int default_val, + const char *detailmsg, LOCKMODE lockmode) +{ + relopt_enum *newoption; + + newoption = (relopt_enum *) allocate_reloption(kinds, RELOPT_TYPE_ENUM, + name, desc, lockmode); + newoption->members = members; + newoption->default_val = default_val; + newoption->detailmsg = detailmsg; + + return newoption; +} + + +/* * add_enum_reloption * Add a new enum reloption * @@ -827,29 +995,42 @@ add_enum_reloption(bits32 kinds, const char *name, const char *desc, relopt_enum_elt_def *members, int default_val, const char *detailmsg, LOCKMODE lockmode) { - relopt_enum *newoption; - - newoption = (relopt_enum *) allocate_reloption(kinds, RELOPT_TYPE_ENUM, - name, desc, lockmode); - newoption->members = members; - newoption->default_val = default_val; - newoption->detailmsg = detailmsg; + relopt_enum *newoption = init_enum_reloption(kinds, name, desc, + members, default_val, + detailmsg, lockmode); add_reloption((relopt_gen *) newoption); } /* - * add_string_reloption - * Add a new string reloption + * add_local_enum_reloption + * Add a new local enum reloption * - * "validator" is an optional function pointer that can be used to test the - * validity of the values. It must elog(ERROR) when the argument string is - * not acceptable for the variable. Note that the default value must pass - * the validation. + * 'offset' is offset of int-typed field. */ void -add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val, - validate_string_relopt validator, LOCKMODE lockmode) +add_local_enum_reloption(local_relopts *relopts, const char *name, + const char *desc, relopt_enum_elt_def *members, + int default_val, const char *detailmsg, int offset) +{ + relopt_enum *newoption = init_enum_reloption(RELOPT_KIND_LOCAL, + name, desc, + members, default_val, + detailmsg, 0); + + add_local_reloption(relopts, (relopt_gen *) newoption, offset); +} + +/* + * init_string_reloption + * Allocate and initialize a new string reloption + */ +static relopt_string * +init_string_reloption(bits32 kinds, const char *name, const char *desc, + const char *default_val, + validate_string_relopt validator, + fill_string_relopt filler, + LOCKMODE lockmode) { relopt_string *newoption; @@ -860,10 +1041,13 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING, name, desc, lockmode); newoption->validate_cb = validator; + newoption->fill_cb = filler; if (default_val) { - newoption->default_val = MemoryContextStrdup(TopMemoryContext, - default_val); + if (kinds == RELOPT_KIND_LOCAL) + newoption->default_val = strdup(default_val); + else + newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val); newoption->default_len = strlen(default_val); newoption->default_isnull = false; } @@ -874,10 +1058,54 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha newoption->default_isnull = true; } + return newoption; +} + +/* + * add_string_reloption + * Add a new string reloption + * + * "validator" is an optional function pointer that can be used to test the + * validity of the values. It must elog(ERROR) when the argument string is + * not acceptable for the variable. Note that the default value must pass + * the validation. + */ +void +add_string_reloption(bits32 kinds, const char *name, const char *desc, + const char *default_val, validate_string_relopt validator, + LOCKMODE lockmode) +{ + relopt_string *newoption = init_string_reloption(kinds, name, desc, + default_val, + validator, NULL, + lockmode); + add_reloption((relopt_gen *) newoption); } /* + * add_local_string_reloption + * Add a new local string reloption + * + * 'offset' is offset of int-typed field that will store offset of string value + * in the resulting bytea structure. + */ +void +add_local_string_reloption(local_relopts *relopts, const char *name, + const char *desc, const char *default_val, + validate_string_relopt validator, + fill_string_relopt filler, int offset) +{ + relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL, + name, desc, + default_val, + validator, filler, + 0); + + add_local_reloption(relopts, (relopt_gen *) newoption, offset); +} + +/* * Transform a relation options list (list of DefElem) into the text array * format that is kept in pg_class.reloptions, including only those options * that are in the passed namespace. The output values do not include the @@ -1173,6 +1401,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, TYPALIGN_INT, + &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. * @@ -1227,57 +1509,35 @@ 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, TYPALIGN_INT, - &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; + *numrelopts = numoptions; + return reloptions; +} - 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; - } - } +/* Parse local unregistered options. */ +static relopt_value * +parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate) +{ + int nopts = list_length(relopts->options); + relopt_value *values = palloc(sizeof(*values) * nopts); + ListCell *lc; + int i = 0; - if (j >= numoptions && validate) - { - char *s; - char *p; + foreach(lc, relopts->options) + { + local_relopt *opt = lfirst(lc); - s = TextDatumGetCString(optiondatums[i]); - p = strchr(s, '='); - if (p) - *p = '\0'; - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized parameter \"%s\"", s))); - } - } + values[i].gen = opt->option; + values[i].isset = false; - /* It's worth avoiding memory leaks in this function */ - pfree(optiondatums); - if (((void *) array) != DatumGetPointer(options)) - pfree(array); + i++; } - *numrelopts = numoptions; - return reloptions; + if (options != (Datum) 0) + parseRelOptionsInternal(options, validate, values, nopts); + + return values; } /* @@ -1424,8 +1684,24 @@ allocateReloptStruct(Size base, relopt_value *options, int numoptions) int i; for (i = 0; i < numoptions; i++) - if (options[i].gen->type == RELOPT_TYPE_STRING) - size += GET_STRING_RELOPTION_LEN(options[i]) + 1; + { + relopt_value *optval = &options[i]; + + if (optval->gen->type == RELOPT_TYPE_STRING) + { + relopt_string *optstr = (relopt_string *) optval->gen; + + if (optstr->fill_cb) + { + const char *val = optval->isset ? optval->values.string_val : + optstr->default_isnull ? NULL : optstr->default_val; + + size += optstr->fill_cb(val, NULL); + } + else + size += GET_STRING_RELOPTION_LEN(*optval) + 1; + } + } return palloc0(size); } @@ -1494,7 +1770,21 @@ fillRelOptions(void *rdopts, Size basesize, else string_val = NULL; - if (string_val == NULL) + if (optstring->fill_cb) + { + Size size = + optstring->fill_cb(string_val, + (char *) rdopts + offset); + + if (size) + { + *(int *) itempos = offset; + offset += size; + } + else + *(int *) itempos = 0; + } + else if (string_val == NULL) *(int *) itempos = 0; else { @@ -1626,6 +1916,46 @@ build_reloptions(Datum reloptions, bool validate, } /* + * 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 * +build_local_reloptions(local_relopts *relopts, Datum options, bool validate) +{ + int noptions = list_length(relopts->options); + relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions); + relopt_value *vals; + void *opts; + int i = 0; + ListCell *lc; + + foreach(lc, relopts->options) + { + local_relopt *opt = lfirst(lc); + + elems[i].optname = opt->option->name; + elems[i].opttype = opt->option->type; + elems[i].offset = opt->offset; + + i++; + } + + vals = parseLocalRelOptions(relopts, options, validate); + opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions); + fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate, + elems, noptions); + + foreach(lc, relopts->validators) + ((relopts_validator) lfirst(lc)) (opts, vals, noptions); + + if (elems) + pfree(elems); + + return opts; +} + +/* * Option parser for partitioned tables */ bytea * diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index a7e55caf28d..a400f1fedbc 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -41,6 +41,7 @@ ginhandler(PG_FUNCTION_ARGS) amroutine->amstrategies = 0; amroutine->amsupport = GINNProcs; + amroutine->amoptsprocnum = GIN_OPTIONS_PROC; amroutine->amcanorder = false; amroutine->amcanorderbyop = false; amroutine->amcanbackward = false; diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c index 0b62e0a8ae8..1e3046f4eb7 100644 --- a/src/backend/access/gin/ginvalidate.c +++ b/src/backend/access/gin/ginvalidate.c @@ -142,6 +142,9 @@ ginvalidate(Oid opclassoid) INTERNALOID, INTERNALOID, INTERNALOID); break; + case GIN_OPTIONS_PROC: + ok = check_amoptsproc_signature(procform->amproc); + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -237,7 +240,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_OPTIONS_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/access/gist/gist.c b/src/backend/access/gist/gist.c index 90c46e86a19..9eee5381aea 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -62,6 +62,7 @@ gisthandler(PG_FUNCTION_ARGS) amroutine->amstrategies = 0; amroutine->amsupport = GISTNProcs; + amroutine->amoptsprocnum = GIST_OPTIONS_PROC; amroutine->amcanorder = false; amroutine->amcanorderbyop = true; amroutine->amcanbackward = false; diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c index 0c4fb8c1bf9..a285736a810 100644 --- a/src/backend/access/gist/gistvalidate.c +++ b/src/backend/access/gist/gistvalidate.c @@ -140,6 +140,9 @@ gistvalidate(Oid opclassoid) 5, 5, INTERNALOID, opcintype, INT2OID, OIDOID, INTERNALOID); break; + case GIST_OPTIONS_PROC: + ok = check_amoptsproc_signature(procform->amproc); + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -259,7 +262,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_OPTIONS_PROC) continue; /* optional methods */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c index 4871b7ff4d6..3ec6d528e77 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -59,6 +59,7 @@ hashhandler(PG_FUNCTION_ARGS) amroutine->amstrategies = HTMaxStrategyNumber; amroutine->amsupport = HASHNProcs; + amroutine->amoptsprocnum = HASHOPTIONS_PROC; amroutine->amcanorder = false; amroutine->amcanorderbyop = false; amroutine->amcanbackward = true; diff --git a/src/backend/access/hash/hashvalidate.c b/src/backend/access/hash/hashvalidate.c index 6346e658652..7b08ed53543 100644 --- a/src/backend/access/hash/hashvalidate.c +++ b/src/backend/access/hash/hashvalidate.c @@ -126,6 +126,10 @@ hashvalidate(Oid opclassoid) procform->amproclefttype); } break; + case HASHOPTIONS_PROC: + if (!check_amoptsproc_signature(procform->amproc)) + result = false; + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/backend/access/index/amvalidate.c b/src/backend/access/index/amvalidate.c index 3eae6aa012f..24d49750ada 100644 --- a/src/backend/access/index/amvalidate.c +++ b/src/backend/access/index/amvalidate.c @@ -21,6 +21,7 @@ #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" +#include "catalog/pg_type.h" #include "parser/parse_coerce.h" #include "utils/syscache.h" @@ -183,6 +184,16 @@ check_amproc_signature(Oid funcid, Oid restype, bool exact, } /* + * Validate the signature of an opclass options support function, that should + * be 'void(internal)'. + */ +bool +check_amoptsproc_signature(Oid funcid) +{ + return check_amproc_signature(funcid, VOIDOID, true, 1, 1, INTERNALOID); +} + +/* * Validate the signature (argument and result types) of an opclass operator. * Return true if OK, false if not. * diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index a5210d0b342..f7e4c65d99f 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -45,17 +45,23 @@ #include "access/amapi.h" #include "access/heapam.h" +#include "access/reloptions.h" #include "access/relscan.h" #include "access/tableam.h" #include "access/transam.h" #include "access/xlog.h" #include "catalog/index.h" +#include "catalog/pg_amproc.h" #include "catalog/pg_type.h" +#include "commands/defrem.h" +#include "nodes/makefuncs.h" #include "pgstat.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/predicate.h" +#include "utils/ruleutils.h" #include "utils/snapmgr.h" +#include "utils/syscache.h" /* ---------------------------------------------------------------- @@ -767,9 +773,9 @@ index_getprocid(Relation irel, nproc = irel->rd_indam->amsupport; - Assert(procnum > 0 && procnum <= (uint16) nproc); + Assert(procnum >= 0 && procnum <= (uint16) nproc); - procindex = (nproc * (attnum - 1)) + (procnum - 1); + procindex = ((nproc + 1) * (attnum - 1)) + procnum; loc = irel->rd_support; @@ -797,13 +803,15 @@ index_getprocinfo(Relation irel, { FmgrInfo *locinfo; int nproc; + int optsproc; int procindex; nproc = irel->rd_indam->amsupport; + optsproc = irel->rd_indam->amoptsprocnum; - Assert(procnum > 0 && procnum <= (uint16) nproc); + Assert(procnum >= 0 && procnum <= (uint16) nproc); - procindex = (nproc * (attnum - 1)) + (procnum - 1); + procindex = ((nproc + 1) * (attnum - 1)) + procnum; locinfo = irel->rd_supportinfo; @@ -832,6 +840,17 @@ index_getprocinfo(Relation irel, procnum, attnum, RelationGetRelationName(irel)); fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt); + + if (procnum != optsproc) + { + /* Initialize locinfo->fn_expr with opclass options Const */ + bytea **attoptions = RelationGetIndexAttOptions(irel, false); + MemoryContext oldcxt = MemoryContextSwitchTo(irel->rd_indexcxt); + + set_fn_opclass_options(locinfo, attoptions[attnum - 1]); + + MemoryContextSwitchTo(oldcxt); + } } return locinfo; @@ -906,3 +925,53 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes, } } } + +/* ---------------- + * index_opclass_options + * + * Parse opclass-specific options for index column. + * ---------------- + */ +bytea * +index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions, + bool validate) +{ + int amoptsprocnum = indrel->rd_indam->amoptsprocnum; + Oid procid = index_getprocid(indrel, attnum, amoptsprocnum); + FmgrInfo *procinfo; + local_relopts relopts; + + if (!OidIsValid(procid)) + { + Oid opclass; + Datum indclassDatum; + oidvector *indclass; + bool isnull; + + if (!DatumGetPointer(attoptions)) + return NULL; /* ok, no options, no procedure */ + + /* + * Report an error if the opclass's options-parsing procedure does not + * exist but the opclass options are specified. + */ + indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple, + Anum_pg_index_indclass, &isnull); + Assert(!isnull); + indclass = (oidvector *) DatumGetPointer(indclassDatum); + opclass = indclass->values[attnum - 1]; + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("operator class %s has no options", + generate_opclass_name(opclass)))); + } + + init_local_reloptions(&relopts, 0); + + procinfo = index_getprocinfo(indrel, attnum, amoptsprocnum); + + (void) FunctionCall1(procinfo, PointerGetDatum(&relopts)); + + return build_local_reloptions(&relopts, attoptions, validate); +} diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index 4bb16297c31..36294789f3f 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -112,6 +112,7 @@ bthandler(PG_FUNCTION_ARGS) amroutine->amstrategies = BTMaxStrategyNumber; amroutine->amsupport = BTNProcs; + amroutine->amoptsprocnum = BTOPTIONS_PROC; amroutine->amcanorder = true; amroutine->amcanorderbyop = false; amroutine->amcanbackward = true; diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c index 627f74407a3..02905f79c82 100644 --- a/src/backend/access/nbtree/nbtvalidate.c +++ b/src/backend/access/nbtree/nbtvalidate.c @@ -108,6 +108,9 @@ btvalidate(Oid opclassoid) ok = check_amproc_signature(procform->amproc, BOOLOID, true, 1, 1, OIDOID); break; + case BTOPTIONS_PROC: + ok = check_amoptsproc_signature(procform->amproc); + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/backend/access/spgist/spgvalidate.c b/src/backend/access/spgist/spgvalidate.c index e316d6eda22..3c433e94e76 100644 --- a/src/backend/access/spgist/spgvalidate.c +++ b/src/backend/access/spgist/spgvalidate.c @@ -159,6 +159,9 @@ spgvalidate(Oid opclassoid) configOut.leafType, true, 1, 1, procform->amproclefttype); break; + case SPGIST_OPTIONS_PROC: + ok = check_amoptsproc_signature(procform->amproc); + break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -271,6 +274,8 @@ spgvalidate(Oid opclassoid) { if ((thisgroup->functionset & (((uint64) 1) << i)) != 0) continue; /* got it */ + if (i == SPGIST_OPTIONS_PROC) + continue; /* optional method */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("operator family \"%s\" of access method %s is missing support function %d for type %s", diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 9d9e9159796..632c058b80a 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -725,6 +725,7 @@ CheckAttributeType(const char *attname, void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, + Datum attoptions, CatalogIndexState indstate) { Datum values[Natts_pg_attribute]; @@ -756,10 +757,11 @@ InsertPgAttributeTuple(Relation pg_attribute_rel, values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal); values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount); values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation); + values[Anum_pg_attribute_attoptions - 1] = attoptions; /* start out with empty permissions and empty options */ nulls[Anum_pg_attribute_attacl - 1] = true; - nulls[Anum_pg_attribute_attoptions - 1] = true; + nulls[Anum_pg_attribute_attoptions - 1] = attoptions == (Datum) 0; nulls[Anum_pg_attribute_attfdwoptions - 1] = true; nulls[Anum_pg_attribute_attmissingval - 1] = true; @@ -813,7 +815,7 @@ AddNewAttributeTuples(Oid new_rel_oid, /* Make sure this is OK, too */ attr->attstattarget = -1; - InsertPgAttributeTuple(rel, attr, indstate); + InsertPgAttributeTuple(rel, attr, (Datum) 0, indstate); /* Add dependency info */ myself.classId = RelationRelationId; @@ -851,7 +853,7 @@ AddNewAttributeTuples(Oid new_rel_oid, /* Fill in the correct relation OID in the copied tuple */ attStruct.attrelid = new_rel_oid; - InsertPgAttributeTuple(rel, &attStruct, indstate); + InsertPgAttributeTuple(rel, &attStruct, (Datum) 0, indstate); } } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 2d81bc3cbc9..bd7ec923e94 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -26,6 +26,7 @@ #include "access/amapi.h" #include "access/heapam.h" #include "access/multixact.h" +#include "access/reloptions.h" #include "access/relscan.h" #include "access/sysattr.h" #include "access/tableam.h" @@ -105,7 +106,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, Oid *classObjectId); static void InitializeAttributeOids(Relation indexRelation, int numatts, Oid indexoid); -static void AppendAttributeTuples(Relation indexRelation, int numatts); +static void AppendAttributeTuples(Relation indexRelation, int numatts, + Datum *attopts); static void UpdateIndexRelation(Oid indexoid, Oid heapoid, Oid parentIndexId, IndexInfo *indexInfo, @@ -484,7 +486,7 @@ InitializeAttributeOids(Relation indexRelation, * ---------------------------------------------------------------- */ static void -AppendAttributeTuples(Relation indexRelation, int numatts) +AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts) { Relation pg_attribute; CatalogIndexState indstate; @@ -506,10 +508,11 @@ AppendAttributeTuples(Relation indexRelation, int numatts) for (i = 0; i < numatts; i++) { Form_pg_attribute attr = TupleDescAttr(indexTupDesc, i); + Datum attoptions = attopts ? attopts[i] : (Datum) 0; Assert(attr->attnum == i + 1); - InsertPgAttributeTuple(pg_attribute, attr, indstate); + InsertPgAttributeTuple(pg_attribute, attr, attoptions, indstate); } CatalogCloseIndexes(indstate); @@ -589,6 +592,7 @@ UpdateIndexRelation(Oid indexoid, else predDatum = (Datum) 0; + /* * open the system catalog index relation */ @@ -976,7 +980,8 @@ index_create(Relation heapRelation, /* * append ATTRIBUTE tuples for the index */ - AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs); + AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs, + indexInfo->ii_OpclassOptions); /* ---------------- * update pg_index @@ -1189,6 +1194,13 @@ index_create(Relation heapRelation, indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs; + /* Validate opclass-specific options */ + if (indexInfo->ii_OpclassOptions) + for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++) + (void) index_opclass_options(indexRelation, i + 1, + indexInfo->ii_OpclassOptions[i], + true); + /* * If this is bootstrap (initdb) time, then we don't actually fill in the * index yet. We'll be creating more indexes and classes later, so we @@ -2336,6 +2348,8 @@ BuildIndexInfo(Relation index) &ii->ii_ExclusionStrats); } + ii->ii_OpclassOptions = RelationGetIndexRawAttOptions(index); + return ii; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 3239185b425..3f7ab8d389b 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -304,6 +304,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; + indexInfo->ii_OpclassOptions = NULL; indexInfo->ii_Unique = true; indexInfo->ii_ReadyForInserts = true; indexInfo->ii_Concurrent = false; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 4e8263af4be..2e5997b5c3c 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -90,6 +90,7 @@ static void RangeVarCallbackForReindexIndex(const RangeVar *relation, static bool ReindexRelationConcurrently(Oid relationOid, int options); static void ReindexPartitionedIndex(Relation parentIdx); static void update_relispartition(Oid relationId, bool newval); +static bool CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts); /* * callback argument type for RangeVarCallbackForReindexIndex() @@ -268,6 +269,18 @@ CheckIndexCompatible(Oid oldId, } } + /* Any change in opclass options break compatibility. */ + if (ret) + { + Datum *opclassOptions = RelationGetIndexRawAttOptions(irel); + + ret = CompareOpclassOptions(opclassOptions, + indexInfo->ii_OpclassOptions, old_natts); + + if (opclassOptions) + pfree(opclassOptions); + } + /* Any change in exclusion operator selections breaks compatibility. */ if (ret && indexInfo->ii_ExclusionOps != NULL) { @@ -302,6 +315,42 @@ CheckIndexCompatible(Oid oldId, return ret; } +/* + * CompareOpclassOptions + * + * Compare per-column opclass options which are represented by arrays of text[] + * datums. Both elements of arrays and array themselves can be NULL. + */ +static bool +CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts) +{ + int i; + + if (!opts1 && !opts2) + return true; + + for (i = 0; i < natts; i++) + { + Datum opt1 = opts1 ? opts1[i] : (Datum) 0; + Datum opt2 = opts2 ? opts2[i] : (Datum) 0; + + if (opt1 == (Datum) 0) + { + if (opt2 == (Datum) 0) + continue; + else + return false; + } + else if (opt2 == (Datum) 0) + return false; + + /* Compare non-NULL text[] datums. */ + if (!DatumGetBool(DirectFunctionCall2(array_eq, opt1, opt2))) + return false; + } + + return true; +} /* * WaitForOlderSnapshots @@ -1528,7 +1577,7 @@ CheckPredicate(Expr *predicate) /* * Compute per-index-column information, including indexed column numbers - * or index expressions, opclasses, and indoptions. Note, all output vectors + * or index expressions, opclasses and their options. Note, all output vectors * should be allocated for all columns, including "including" ones. */ static void @@ -1829,6 +1878,20 @@ ComputeIndexAttrs(IndexInfo *indexInfo, accessMethodName))); } + /* Set up the per-column opclass options (attoptions field). */ + if (attribute->opclassopts) + { + Assert(attn < nkeycols); + + if (!indexInfo->ii_OpclassOptions) + indexInfo->ii_OpclassOptions = + palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs); + + indexInfo->ii_OpclassOptions[attn] = + transformRelOptions((Datum) 0, attribute->opclassopts, + NULL, NULL, false, false); + } + attn++; } } diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index 743511bdf21..f1026de7565 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -53,14 +53,15 @@ static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, int maxOpNumber, int maxProcNumber, - List *items); + int opclassOptsProcNumber, List *items); static void AlterOpFamilyDrop(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, int maxOpNumber, int maxProcNumber, List *items); static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype); static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid); -static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid); +static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid, + int opclassOptsProcNum); static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc); static void storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, Oid opclassoid, @@ -337,6 +338,7 @@ DefineOpClass(CreateOpClassStmt *stmt) opfamilyoid, /* oid of containing opfamily */ opclassoid; /* oid of opclass we create */ int maxOpNumber, /* amstrategies value */ + optsProcNumber, /* amoptsprocnum value */ maxProcNumber; /* amsupport value */ bool amstorage; /* amstorage flag */ List *operators; /* OpFamilyMember list for operators */ @@ -381,6 +383,7 @@ DefineOpClass(CreateOpClassStmt *stmt) if (maxOpNumber <= 0) maxOpNumber = SHRT_MAX; maxProcNumber = amroutine->amsupport; + optsProcNumber = amroutine->amoptsprocnum; amstorage = amroutine->amstorage; /* XXX Should we make any privilege check against the AM? */ @@ -536,7 +539,6 @@ DefineOpClass(CreateOpClassStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, get_func_name(funcOid)); #endif - /* Save the info */ member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); member->object = funcOid; @@ -547,7 +549,7 @@ DefineOpClass(CreateOpClassStmt *stmt) processTypesSpec(item->class_args, &member->lefttype, &member->righttype); - assignProcTypes(member, amoid, typeoid); + assignProcTypes(member, amoid, typeoid, optsProcNumber); addFamilyMember(&procedures, member, true); break; case OPCLASS_ITEM_STORAGETYPE: @@ -777,6 +779,7 @@ AlterOpFamily(AlterOpFamilyStmt *stmt) Oid amoid, /* our AM's oid */ opfamilyoid; /* oid of opfamily */ int maxOpNumber, /* amstrategies value */ + optsProcNumber, /* amopclassopts value */ maxProcNumber; /* amsupport value */ HeapTuple tup; Form_pg_am amform; @@ -800,6 +803,7 @@ AlterOpFamily(AlterOpFamilyStmt *stmt) if (maxOpNumber <= 0) maxOpNumber = SHRT_MAX; maxProcNumber = amroutine->amsupport; + optsProcNumber = amroutine->amoptsprocnum; /* XXX Should we make any privilege check against the AM? */ @@ -824,7 +828,8 @@ AlterOpFamily(AlterOpFamilyStmt *stmt) maxOpNumber, maxProcNumber, stmt->items); else AlterOpFamilyAdd(stmt, amoid, opfamilyoid, - maxOpNumber, maxProcNumber, stmt->items); + maxOpNumber, maxProcNumber, optsProcNumber, + stmt->items); return opfamilyoid; } @@ -834,7 +839,8 @@ AlterOpFamily(AlterOpFamilyStmt *stmt) */ static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, - int maxOpNumber, int maxProcNumber, List *items) + int maxOpNumber, int maxProcNumber, int optsProcNumber, + List *items) { List *operators; /* OpFamilyMember list for operators */ List *procedures; /* OpFamilyMember list for support procs */ @@ -926,7 +932,7 @@ AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, processTypesSpec(item->class_args, &member->lefttype, &member->righttype); - assignProcTypes(member, amoid, InvalidOid); + assignProcTypes(member, amoid, InvalidOid, optsProcNumber); addFamilyMember(&procedures, member, true); break; case OPCLASS_ITEM_STORAGETYPE: @@ -1129,7 +1135,8 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid) * and do any validity checking we can manage. */ static void -assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid) +assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid, + int opclassOptsProcNum) { HeapTuple proctup; Form_pg_proc procform; @@ -1140,6 +1147,36 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid) elog(ERROR, "cache lookup failed for function %u", member->object); procform = (Form_pg_proc) GETSTRUCT(proctup); + /* Check the signature of the opclass options parsing function */ + if (member->number == opclassOptsProcNum) + { + if (OidIsValid(typeoid)) + { + if ((OidIsValid(member->lefttype) && member->lefttype != typeoid) || + (OidIsValid(member->righttype) && member->righttype != typeoid)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("associated data types for opclass options " + "parsing functions must match opclass input type"))); + } + else + { + if (member->lefttype != member->righttype) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("left and right associated data types for " + "opclass options parsing functions must match"))); + } + + if (procform->prorettype != VOIDOID || + procform->pronargs != 1 || + procform->proargtypes.values[0] != INTERNALOID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("invalid opclass options parsing function"), + errhint("opclass options parsing function must have signature '%s'", + "(internal) RETURNS void"))); + } /* * btree comparison procs must be 2-arg procs returning int4. btree * sortsupport procs must take internal and return void. btree in_range @@ -1148,7 +1185,7 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid) * returning int4, while proc 2 must be a 2-arg proc returning int8. * Otherwise we don't know. */ - if (amoid == BTREE_AM_OID) + else if (amoid == BTREE_AM_OID) { if (member->number == BTORDER_PROC) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 8e35c5bd1a2..c8c88be2c9a 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -6085,7 +6085,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, ReleaseSysCache(typeTuple); - InsertPgAttributeTuple(attrdesc, &attribute, NULL); + InsertPgAttributeTuple(attrdesc, &attribute, (Datum) 0, NULL); table_close(attrdesc, RowExclusiveLock); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index eaab97f7535..c9a90d11915 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2877,6 +2877,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 88b912977e9..d05ca26fcf5 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -2572,6 +2572,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b) COMPARE_STRING_FIELD(indexcolname); COMPARE_NODE_FIELD(collation); COMPARE_NODE_FIELD(opclass); + COMPARE_NODE_FIELD(opclassopts); COMPARE_SCALAR_FIELD(ordering); COMPARE_SCALAR_FIELD(nulls_ordering); diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index e8cdc90c315..b442b5a29ef 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -763,6 +763,9 @@ makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions, n->ii_ExclusionProcs = NULL; n->ii_ExclusionStrats = NULL; + /* opclass options */ + n->ii_OpclassOptions = NULL; + /* speculative inserts */ n->ii_UniqueOps = NULL; n->ii_UniqueProcs = NULL; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index e084c3f0699..eb168ffd6da 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2869,6 +2869,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 d82fc5ab8b8..51470dd73e1 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -278,6 +278,9 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, info->amcostestimate = amroutine->amcostestimate; Assert(info->amcostestimate != NULL); + /* Fetch index opclass options */ + info->opclassoptions = RelationGetIndexAttOptions(indexRelation, true); + /* * Fetch the ordering information for the index, if any. */ diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 7e384f956c8..eb0bf12cd8b 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -493,7 +493,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <alias> alias_clause opt_alias_clause %type <list> func_alias_clause %type <sortby> sortby -%type <ielem> index_elem +%type <ielem> index_elem index_elem_options %type <node> table_ref %type <jexpr> joined_table %type <range> relation_expr @@ -7478,43 +7478,53 @@ 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; + } + ; + /* * Index attributes can be either simple column references, or arbitrary * expressions in parens. For backwards-compatibility reasons, we allow * an expression that's just a function call to be written without parens. */ -index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order +index_elem: ColId index_elem_options { - $$ = makeNode(IndexElem); + $$ = $2; $$->name = $1; - $$->expr = NULL; - $$->indexcolname = NULL; - $$->collation = $2; - $$->opclass = $3; - $$->ordering = $4; - $$->nulls_ordering = $5; } - | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order + | func_expr_windowless index_elem_options { - $$ = makeNode(IndexElem); - $$->name = NULL; + $$ = $2; $$->expr = $1; - $$->indexcolname = NULL; - $$->collation = $2; - $$->opclass = $3; - $$->ordering = $4; - $$->nulls_ordering = $5; } - | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order + | '(' a_expr ')' index_elem_options { - $$ = makeNode(IndexElem); - $$->name = NULL; + $$ = $4; $$->expr = $2; - $$->indexcolname = NULL; - $$->collation = $4; - $$->opclass = $5; - $$->ordering = $6; - $$->nulls_ordering = $7; } ; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index c1911411d0b..ae322aae567 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1591,6 +1591,8 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, /* Add the operator class name, if non-default */ iparam->opclass = get_opclass(indclass->values[keyno], keycoltype); + iparam->opclassopts = + untransformRelOptions(get_attoptions(source_relid, keyno + 1)); iparam->ordering = SORTBY_DEFAULT; iparam->nulls_ordering = SORTBY_NULLS_DEFAULT; @@ -2168,10 +2170,14 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) * constraint; and there's also the dump/reload problem * mentioned above. */ + Datum attoptions = + get_attoptions(RelationGetRelid(index_rel), i + 1); + defopclass = GetDefaultOpClass(attform->atttypid, index_rel->rd_rel->relam); if (indclass->values[i] != defopclass || attform->attcollation != index_rel->rd_indcollation[i] || + attoptions != (Datum) 0 || index_rel->rd_indoption[i] != 0) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -2351,6 +2357,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) iparam->indexcolname = NULL; iparam->collation = NIL; iparam->opclass = NIL; + iparam->opclassopts = NIL; iparam->ordering = SORTBY_DEFAULT; iparam->nulls_ordering = SORTBY_NULLS_DEFAULT; index->indexParams = lappend(index->indexParams, iparam); @@ -2464,6 +2471,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) iparam->indexcolname = NULL; iparam->collation = NIL; iparam->opclass = NIL; + iparam->opclassopts = NIL; index->indexIncludingParams = lappend(index->indexIncludingParams, iparam); } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 5e63238f030..f6cf7e72a1e 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -480,6 +480,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 ") @@ -1384,6 +1385,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, { int16 opt = indoption->values[keyno]; Oid indcoll = indcollation->values[keyno]; + Datum attoptions = get_attoptions(indexrelid, keyno + 1); + bool has_options = attoptions != (Datum) 0; /* Add collation, if not default for column */ if (OidIsValid(indcoll) && indcoll != keycolcollation) @@ -1391,7 +1394,15 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, generate_collation_name((indcoll))); /* Add the operator class name, if not default */ - get_opclass_name(indclass->values[keyno], keycoltype, &buf); + get_opclass_name(indclass->values[keyno], + has_options ? InvalidOid : keycoltype, &buf); + + if (has_options) + { + appendStringInfoString(&buf, " ("); + get_reloptions(&buf, attoptions); + appendStringInfoChar(&buf, ')'); + } /* Add options if relevant */ if (amroutine->amcanorder) @@ -10574,6 +10585,23 @@ get_opclass_name(Oid opclass, Oid actual_datatype, } /* + * generate_opclass_name + * Compute the name to display for a opclass specified by OID + * + * The result includes all necessary quoting and schema-prefixing. + */ +char * +generate_opclass_name(Oid opclass) +{ + StringInfoData buf; + + initStringInfo(&buf); + get_opclass_name(opclass, InvalidOid, &buf); + + return &buf.data[1]; /* get_opclass_name() prepends space */ +} + +/* * processIndirection - take care of array and subfield assignment * * We strip any top-level FieldStore or assignment SubscriptingRef nodes that @@ -11251,6 +11279,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, TYPALIGN_INT, + &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 * @@ -11270,56 +11354,9 @@ flatten_reloptions(Oid relid) if (!isnull) { StringInfoData buf; - Datum *options; - int noptions; - int i; initStringInfo(&buf); - - deconstruct_array(DatumGetArrayTypeP(reloptions), - TEXTOID, -1, false, TYPALIGN_INT, - &options, NULL, &noptions); - - for (i = 0; i < noptions; i++) - { - char *option = TextDatumGetCString(options[i]); - char *name; - char *separator; - char *value; - - /* - * Each array element should have the form name=value. If the "=" - * is missing for some reason, treat it like an empty value. - */ - name = option; - separator = strchr(option, '='); - if (separator) - { - *separator = '\0'; - value = separator + 1; - } - else - value = ""; - - if (i > 0) - appendStringInfoString(&buf, ", "); - appendStringInfo(&buf, "%s=", quote_identifier(name)); - - /* - * In general we need to quote the value; but to avoid unnecessary - * clutter, do not quote if it is an identifier that would not - * need quoting. (We could also allow numbers, but that is a bit - * trickier than it looks --- for example, are leading zeroes - * significant? We don't want to assume very much here about what - * custom reloptions might mean.) - */ - if (quote_identifier(value) == value) - appendStringInfoString(&buf, value); - else - simple_quote_literal(&buf, value); - - pfree(option); - } + get_reloptions(&buf, reloptions); result = buf.data; } diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 8339f4cd7a2..e62b69d6f26 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -6367,6 +6367,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol, Oid clause_op, Datum query, GinQualCounts *counts) { + FmgrInfo flinfo; Oid extractProcOid; Oid collation; int strategy_op; @@ -6416,15 +6417,19 @@ gincost_pattern(IndexOptInfo *index, int indexcol, else collation = DEFAULT_COLLATION_OID; - OidFunctionCall7Coll(extractProcOid, - collation, - query, - PointerGetDatum(&nentries), - UInt16GetDatum(strategy_op), - PointerGetDatum(&partial_matches), - PointerGetDatum(&extra_data), - PointerGetDatum(&nullFlags), - PointerGetDatum(&searchMode)); + fmgr_info(extractProcOid, &flinfo); + + set_fn_opclass_options(&flinfo, index->opclassoptions[indexcol]); + + FunctionCall7Coll(&flinfo, + collation, + query, + PointerGetDatum(&nentries), + UInt16GetDatum(strategy_op), + PointerGetDatum(&partial_matches), + PointerGetDatum(&extra_data), + PointerGetDatum(&nullFlags), + PointerGetDatum(&searchMode)); if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT) { diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c index 8320dcda800..2e0bc3ebd07 100644 --- a/src/backend/utils/adt/tsgistidx.c +++ b/src/backend/utils/adt/tsgistidx.c @@ -16,6 +16,7 @@ #include "access/gist.h" #include "access/heaptoast.h" +#include "access/reloptions.h" #include "lib/qunique.h" #include "port/pg_bitutils.h" #include "tsearch/ts_utils.h" @@ -23,17 +24,25 @@ #include "utils/pg_crc.h" -#define SIGLENINT 31 /* >121 => key will toast, so it will not work - * !!! */ +/* tsvector_ops opclass options */ +typedef struct +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int siglen; /* signature length */ +} GistTsVectorOptions; -#define SIGLEN ( sizeof(int32) * SIGLENINT ) -#define SIGLENBIT (SIGLEN * BITS_PER_BYTE) +#define SIGLEN_DEFAULT (31 * 4) +#define SIGLEN_MAX GISTMaxIndexKeySize +#define GET_SIGLEN() (PG_HAS_OPCLASS_OPTIONS() ? \ + ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen : \ + SIGLEN_DEFAULT) + +#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE) -typedef char BITVEC[SIGLEN]; typedef char *BITVECP; -#define LOOPBYTE \ - for(i=0;i<SIGLEN;i++) +#define LOOPBYTE(siglen) \ + for (i = 0; i < siglen; i++) #define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) ) #define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 ) @@ -41,8 +50,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)) @@ -66,13 +75,14 @@ typedef struct #define ISALLTRUE(x) ( ((SignTSVector*)(x))->flag & ALLISTRUE ) #define GTHDRSIZE ( VARHDRSZ + sizeof(int32) ) -#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) ) +#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) ) #define GETSIGN(x) ( (BITVECP)( (char*)(x)+GTHDRSIZE ) ) +#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE ) #define GETARR(x) ( (int32*)( (char*)(x)+GTHDRSIZE ) ) #define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) ) -static int32 sizebitvec(BITVECP sign); +static int32 sizebitvec(BITVECP sign, int siglen); Datum gtsvectorin(PG_FUNCTION_ARGS) @@ -103,9 +113,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); @@ -124,36 +135,49 @@ compareint(const void *va, const void *vb) } 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 = GET_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--) @@ -185,13 +209,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; } @@ -203,22 +223,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, @@ -292,12 +307,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 @@ -324,7 +341,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)); } @@ -342,7 +359,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS) } static int32 -unionkey(BITVECP sbase, SignTSVector *add) +unionkey(BITVECP sbase, SignTSVector *add, int siglen) { int32 i; @@ -353,7 +370,9 @@ unionkey(BITVECP sbase, SignTSVector *add) if (ISALLTRUE(add)) return 1; - LOOPBYTE + Assert(GETSIGLEN(add) == siglen); + + LOOPBYTE(siglen) sbase[i] |= sadd[i]; } else @@ -361,7 +380,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; } @@ -372,30 +391,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 = GET_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); } @@ -406,6 +419,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 = GET_SIGLEN(); if (ISSIGNKEY(a)) { /* then b also ISSIGNKEY */ @@ -421,8 +435,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]) { @@ -459,19 +475,19 @@ gtsvector_same(PG_FUNCTION_ARGS) } static int32 -sizebitvec(BITVECP sign) +sizebitvec(BITVECP sign, int siglen) { - return pg_popcount(sign, SIGLEN); + return pg_popcount(sign, siglen); } static int -hemdistsign(BITVECP a, BITVECP b) +hemdistsign(BITVECP a, BITVECP b, int siglen) { int i, diff, dist = 0; - LOOPBYTE + LOOPBYTE(siglen) { diff = (unsigned char) (a[i] ^ b[i]); /* Using the popcount functions here isn't likely to win */ @@ -483,17 +499,22 @@ hemdistsign(BITVECP a, BITVECP b) static int hemdist(SignTSVector *a, SignTSVector *b) { + int siglena = GETSIGLEN(a); + int siglenb = GETSIGLEN(b); + if (ISALLTRUE(a)) { if (ISALLTRUE(b)) return 0; else - return SIGLENBIT - sizebitvec(GETSIGN(b)); + return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb); } else if (ISALLTRUE(b)) - return SIGLENBIT - sizebitvec(GETSIGN(a)); + return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena); + + Assert(siglena == siglenb); - return hemdistsign(GETSIGN(a), GETSIGN(b)); + return hemdistsign(GETSIGN(a), GETSIGN(b), siglena); } Datum @@ -502,6 +523,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 = GET_SIGLEN(); SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key); SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key); BITVECP orig = GETSIGN(origval); @@ -510,14 +532,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); @@ -527,19 +557,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) ) @@ -563,19 +593,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 @@ -583,6 +613,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); + int siglen = GET_SIGLEN(); OffsetNumber k, j; SignTSVector *datum_l, @@ -602,6 +633,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS) BITVECP ptr; int i; CACHESIGN *cache; + char *cache_sign; SPLITCOST *costvector; maxoff = entryvec->n - 2; @@ -610,16 +642,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; @@ -641,44 +679,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); @@ -704,36 +719,40 @@ 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; @@ -744,12 +763,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; @@ -776,3 +795,16 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS) { return gtsvector_consistent(fcinfo); } + +Datum +gtsvector_options(PG_FUNCTION_ARGS) +{ + local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0); + + init_local_reloptions(relopts, sizeof(GistTsVectorOptions)); + add_local_int_reloption(relopts, "siglen", "signature length", + SIGLEN_DEFAULT, 1, SIGLEN_MAX, + offsetof(GistTsVectorOptions, siglen)); + + PG_RETURN_VOID(); +} diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 27bbb58f564..0a6db0d478e 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -909,6 +909,41 @@ get_atttypetypmodcoll(Oid relid, AttrNumber attnum, ReleaseSysCache(tp); } +/* + * get_attoptions + * + * Given the relation id and the attribute number, + * return the attribute options text[] datum, if any. + */ +Datum +get_attoptions(Oid relid, int16 attnum) +{ + HeapTuple tuple; + Datum attopts; + Datum result; + bool isnull; + + tuple = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum)); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attnum, relid); + + attopts = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions, + &isnull); + + if (isnull) + result = (Datum) 0; + else + result = datumCopy(attopts, false, -1); /* text[] */ + + ReleaseSysCache(tuple); + + return result; +} + /* ---------- PG_CAST CACHE ---------- */ /* diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 782af9aeed2..fa82ab9c5c9 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -1426,7 +1426,7 @@ RelationInitIndexAccessInfo(Relation relation) amsupport = relation->rd_indam->amsupport; if (amsupport > 0) { - int nsupport = indnatts * amsupport; + int nsupport = indnatts * (amsupport + 1); relation->rd_support = (RegProcedure *) MemoryContextAllocZero(indexcxt, nsupport * sizeof(RegProcedure)); @@ -1490,6 +1490,8 @@ RelationInitIndexAccessInfo(Relation relation) indoption = (int2vector *) DatumGetPointer(indoptionDatum); memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16)); + (void) RelationGetIndexAttOptions(relation, false); + /* * expressions, predicate, exclusion caches will be filled later */ @@ -1539,9 +1541,9 @@ IndexSupportInitialize(oidvector *indclass, opFamily[attIndex] = opcentry->opcfamily; opcInType[attIndex] = opcentry->opcintype; if (maxSupportNumber > 0) - memcpy(&indexSupport[attIndex * maxSupportNumber], + memcpy(&indexSupport[attIndex * (maxSupportNumber + 1)], opcentry->supportProcs, - maxSupportNumber * sizeof(RegProcedure)); + (maxSupportNumber + 1) * sizeof(RegProcedure)); } } @@ -1606,7 +1608,7 @@ LookupOpclassInfo(Oid operatorClassOid, if (numSupport > 0) opcentry->supportProcs = (RegProcedure *) MemoryContextAllocZero(CacheMemoryContext, - numSupport * sizeof(RegProcedure)); + (numSupport + 1) * sizeof(RegProcedure)); else opcentry->supportProcs = NULL; } @@ -1693,13 +1695,12 @@ LookupOpclassInfo(Oid operatorClassOid, { Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(htup); - if (amprocform->amprocnum <= 0 || + if (amprocform->amprocnum < 0 || (StrategyNumber) amprocform->amprocnum > numSupport) elog(ERROR, "invalid amproc number %d for opclass %u", amprocform->amprocnum, operatorClassOid); - opcentry->supportProcs[amprocform->amprocnum - 1] = - amprocform->amproc; + opcentry->supportProcs[amprocform->amprocnum] = amprocform->amproc; } systable_endscan(scan); @@ -3980,6 +3981,8 @@ load_critical_index(Oid indexoid, Oid heapoid) ird->rd_refcnt = 1; UnlockRelationOid(indexoid, AccessShareLock); UnlockRelationOid(heapoid, AccessShareLock); + + (void) RelationGetIndexAttOptions(ird, false); } /* @@ -5186,6 +5189,100 @@ GetRelationPublicationActions(Relation relation) } /* + * RelationGetIndexRawAttOptions -- get AM/opclass-specific options for the index + */ +Datum * +RelationGetIndexRawAttOptions(Relation indexrel) +{ + Oid indexrelid = RelationGetRelid(indexrel); + int16 natts = RelationGetNumberOfAttributes(indexrel); + Datum *options = NULL; + int16 attnum; + + for (attnum = 1; attnum <= natts; attnum++) + { + if (!OidIsValid(index_getprocid(indexrel, attnum, + indexrel->rd_indam->amoptsprocnum))) + continue; + + if (!options) + options = palloc0(sizeof(Datum) * natts); + + options[attnum - 1] = get_attoptions(indexrelid, attnum); + } + + return options; +} + +static bytea ** +CopyIndexAttOptions(bytea **srcopts, int natts) +{ + bytea **opts = palloc(sizeof(*opts) * natts); + + for (int i = 0; i < natts; i++) + { + bytea *opt = srcopts[i]; + + opts[i] = !opt ? NULL : (bytea *) + DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1)); + } + + return opts; +} + +/* + * RelationGetIndexAttOptions + * get AM/opclass-specific options for an index parsed into a binary form + */ +bytea ** +RelationGetIndexAttOptions(Relation relation, bool copy) +{ + MemoryContext oldcxt; + bytea **opts = relation->rd_opcoptions; + Oid relid = RelationGetRelid(relation); + int natts = RelationGetNumberOfAttributes(relation); /* XXX IndexRelationGetNumberOfKeyAttributes */ + int i; + + /* Try to copy cached options. */ + if (opts) + return copy ? CopyIndexAttOptions(opts, natts) : opts; + + /* Get and parse opclass options. */ + opts = palloc0(sizeof(*opts) * natts); + + for (i = 0; i < natts; i++) + { + if (criticalRelcachesBuilt && relid != AttributeRelidNumIndexId) + { + Datum attoptions = get_attoptions(relid, i + 1); + + opts[i] = index_opclass_options(relation, i + 1, attoptions, false); + + if (attoptions != (Datum) 0) + pfree(DatumGetPointer(attoptions)); + } + } + + /* Copy parsed options to the cache. */ + oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt); + relation->rd_opcoptions = CopyIndexAttOptions(opts, natts); + MemoryContextSwitchTo(oldcxt); + + if (copy) + return opts; + + for (i = 0; i < natts; i++) + { + if (opts[i]) + pfree(opts[i]); + } + + pfree(opts); + + return relation->rd_opcoptions; +} + +/* * Routines to support ereport() reports of relation-related errors * * These could have been put into elog.c, but it seems like a module layering @@ -5546,8 +5643,25 @@ load_relcache_init_file(bool shared) rel->rd_indoption = indoption; + /* finally, read the vector of opcoptions values */ + rel->rd_opcoptions = (bytea **) + MemoryContextAllocZero(indexcxt, sizeof(*rel->rd_opcoptions) * relform->relnatts); + + for (i = 0; i < relform->relnatts; i++) + { + if (fread(&len, 1, sizeof(len), fp) != sizeof(len)) + goto read_failed; + + if (len > 0) + { + rel->rd_opcoptions[i] = (bytea *) MemoryContextAlloc(indexcxt, len); + if (fread(rel->rd_opcoptions[i], 1, len, fp) != len) + goto read_failed; + } + } + /* set up zeroed fmgr-info vector */ - nsupport = relform->relnatts * rel->rd_indam->amsupport; + nsupport = relform->relnatts * (rel->rd_indam->amsupport + 1); rel->rd_supportinfo = (FmgrInfo *) MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo)); } @@ -5574,6 +5688,7 @@ load_relcache_init_file(bool shared) Assert(rel->rd_supportinfo == NULL); Assert(rel->rd_indoption == NULL); Assert(rel->rd_indcollation == NULL); + Assert(rel->rd_opcoptions == NULL); } /* @@ -5847,7 +5962,7 @@ write_relcache_init_file(bool shared) /* next, write the vector of support procedure OIDs */ write_item(rel->rd_support, - relform->relnatts * (rel->rd_indam->amsupport * sizeof(RegProcedure)), + relform->relnatts * ((rel->rd_indam->amsupport + 1) * sizeof(RegProcedure)), fp); /* next, write the vector of collation OIDs */ @@ -5859,6 +5974,16 @@ write_relcache_init_file(bool shared) write_item(rel->rd_indoption, relform->relnatts * sizeof(int16), fp); + + Assert(rel->rd_opcoptions); + + /* finally, write the vector of opcoptions values */ + for (i = 0; i < relform->relnatts; i++) + { + bytea *opt = rel->rd_opcoptions[i]; + + write_item(opt, opt ? VARSIZE(opt) : 0, fp); + } } } diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 2b4226d3a81..03c614b234a 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -18,9 +18,11 @@ #include "access/detoast.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" +#include "catalog/pg_type.h" #include "executor/functions.h" #include "lib/stringinfo.h" #include "miscadmin.h" +#include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "pgstat.h" #include "utils/acl.h" @@ -1952,6 +1954,57 @@ get_fn_expr_variadic(FmgrInfo *flinfo) return false; } +/* + * Set options to FmgrInfo of opclass support function. + * + * Opclass support functions are called outside of expressions. Thanks to that + * we can use fn_expr to store opclass options as bytea constant. + */ +void +set_fn_opclass_options(FmgrInfo *flinfo, bytea *options) +{ + flinfo->fn_expr = (Node *) makeConst(BYTEAOID, -1, InvalidOid, -1, + PointerGetDatum(options), + options == NULL, false); +} + +/* + * Check if options are defined for opclass support function. + */ +bool +has_fn_opclass_options(FmgrInfo *flinfo) +{ + if (flinfo && flinfo->fn_expr && IsA(flinfo->fn_expr, Const)) + { + Const *expr = (Const *) flinfo->fn_expr; + + if (expr->consttype == BYTEAOID) + return !expr->constisnull; + } + return false; +} + +/* + * Get options for opclass support function. + */ +bytea * +get_fn_opclass_options(FmgrInfo *flinfo) +{ + if (flinfo && flinfo->fn_expr && IsA(flinfo->fn_expr, Const)) + { + Const *expr = (Const *) flinfo->fn_expr; + + if (expr->consttype == BYTEAOID) + return expr->constisnull ? NULL : DatumGetByteaP(expr->constvalue); + } + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("opclass options info is absent in function call context"))); + + return NULL; +} + /*------------------------------------------------------------------------- * Support routines for procedural language implementations *------------------------------------------------------------------------- |