diff options
| -rw-r--r-- | .abi-compliance-history | 8 | ||||
| -rw-r--r-- | contrib/ltree/crc32.c | 47 | ||||
| -rw-r--r-- | contrib/ltree/lquery_op.c | 73 | ||||
| -rw-r--r-- | contrib/ltree/ltree.h | 7 | ||||
| -rw-r--r-- | contrib/ltree/ltxtquery_op.c | 8 | ||||
| -rw-r--r-- | doc/src/sgml/gist.sgml | 4 | ||||
| -rw-r--r-- | src/backend/access/transam/parallel.c | 7 | ||||
| -rw-r--r-- | src/include/utils/pg_locale.h | 14 |
8 files changed, 140 insertions, 28 deletions
diff --git a/.abi-compliance-history b/.abi-compliance-history index 6e84b96d851..4b94ea25f08 100644 --- a/.abi-compliance-history +++ b/.abi-compliance-history @@ -18,6 +18,14 @@ # Be sure to replace "<ADD JUSTIFICATION HERE>" with details of your change and # why it is deemed acceptable. +bae8ca82fd00603ebafa0658640d6e4dfe20af92 +# +# Revisit cosmetics of "For inplace update, send nontransactional invalidations." +# 2025-12-15 12:19:53 -0800 +# +# This removed a CacheInvalidateHeapTupleInplace() parameter. PGXN contained +# no calls to that function. + 00eb646ea43410e5df77fed96f4a981e66811796 # # Check for CREATE privilege on the schema in CREATE STATISTICS. diff --git a/contrib/ltree/crc32.c b/contrib/ltree/crc32.c index 134f46a805e..ce1b0f28e21 100644 --- a/contrib/ltree/crc32.c +++ b/contrib/ltree/crc32.c @@ -10,31 +10,62 @@ #include "postgres.h" #include "ltree.h" +#include "crc32.h" +#include "utils/pg_crc.h" #ifdef LOWER_NODE -#include <ctype.h> -#define TOLOWER(x) tolower((unsigned char) (x)) -#else -#define TOLOWER(x) (x) +#include "catalog/pg_collation.h" +#include "utils/pg_locale.h" #endif -#include "crc32.h" -#include "utils/pg_crc.h" +#ifdef LOWER_NODE unsigned int ltree_crc32_sz(const char *buf, int size) { pg_crc32 crc; const char *p = buf; + static pg_locale_t locale = NULL; + + if (!locale) + locale = pg_newlocale_from_collation(DEFAULT_COLLATION_OID); INIT_TRADITIONAL_CRC32(crc); while (size > 0) { - char c = (char) TOLOWER(*p); + char foldstr[UNICODE_CASEMAP_BUFSZ]; + int srclen = pg_mblen(p); + size_t foldlen; + + /* fold one codepoint at a time */ + foldlen = pg_strfold(foldstr, UNICODE_CASEMAP_BUFSZ, p, srclen, + locale); + + COMP_TRADITIONAL_CRC32(crc, foldstr, foldlen); + + size -= srclen; + p += srclen; + } + FIN_TRADITIONAL_CRC32(crc); + return (unsigned int) crc; +} + +#else - COMP_TRADITIONAL_CRC32(crc, &c, 1); +unsigned int +ltree_crc32_sz(const char *buf, int size) +{ + pg_crc32 crc; + const char *p = buf; + + INIT_TRADITIONAL_CRC32(crc); + while (size > 0) + { + COMP_TRADITIONAL_CRC32(crc, p, 1); size--; p++; } FIN_TRADITIONAL_CRC32(crc); return (unsigned int) crc; } + +#endif /* !LOWER_NODE */ diff --git a/contrib/ltree/lquery_op.c b/contrib/ltree/lquery_op.c index a6466f575fd..9b1de101213 100644 --- a/contrib/ltree/lquery_op.c +++ b/contrib/ltree/lquery_op.c @@ -41,7 +41,8 @@ getlexeme(char *start, char *end, int *len) } bool -compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend) +compare_subnode(ltree_level *t, char *qn, int len, + ltree_prefix_eq_func prefix_eq, bool anyend) { char *endt = t->name + t->len; char *endq = qn + len; @@ -57,7 +58,7 @@ compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, while ((tn = getlexeme(tn, endt, &lent)) != NULL) { if ((lent == lenq || (lent > lenq && anyend)) && - (*cmpptr) (qn, tn, lenq) == 0) + (*prefix_eq) (qn, lenq, tn, lent)) { isok = true; @@ -74,14 +75,62 @@ compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, return true; } -int -ltree_strncasecmp(const char *a, const char *b, size_t s) +/* + * Check if 'a' is a prefix of 'b'. + */ +bool +ltree_prefix_eq(const char *a, size_t a_sz, const char *b, size_t b_sz) +{ + if (a_sz > b_sz) + return false; + else + return (strncmp(a, b, a_sz) == 0); +} + +/* + * Case-insensitive check if 'a' is a prefix of 'b'. + */ +bool +ltree_prefix_eq_ci(const char *a, size_t a_sz, const char *b, size_t b_sz) { - char *al = str_tolower(a, s, DEFAULT_COLLATION_OID); - char *bl = str_tolower(b, s, DEFAULT_COLLATION_OID); - int res; + static pg_locale_t locale = NULL; + size_t al_sz = a_sz + 1; + size_t al_len; + char *al = palloc(al_sz); + size_t bl_sz = b_sz + 1; + size_t bl_len; + char *bl = palloc(bl_sz); + bool res; + + if (!locale) + locale = pg_newlocale_from_collation(DEFAULT_COLLATION_OID); + + /* casefold both a and b */ + + al_len = pg_strfold(al, al_sz, a, a_sz, locale); + if (al_len + 1 > al_sz) + { + /* grow buffer if needed and retry */ + al_sz = al_len + 1; + al = repalloc(al, al_sz); + al_len = pg_strfold(al, al_sz, a, a_sz, locale); + Assert(al_len + 1 <= al_sz); + } + + bl_len = pg_strfold(bl, bl_sz, b, b_sz, locale); + if (bl_len + 1 > bl_sz) + { + /* grow buffer if needed and retry */ + bl_sz = bl_len + 1; + bl = repalloc(bl, bl_sz); + bl_len = pg_strfold(bl, bl_sz, b, b_sz, locale); + Assert(bl_len + 1 <= bl_sz); + } - res = strncmp(al, bl, s); + if (al_len > bl_len) + res = false; + else + res = (strncmp(al, bl, al_len) == 0); pfree(al); pfree(bl); @@ -109,19 +158,19 @@ checkLevel(lquery_level *curq, ltree_level *curt) for (int i = 0; i < curq->numvar; i++) { - int (*cmpptr) (const char *, const char *, size_t); + ltree_prefix_eq_func prefix_eq; - cmpptr = (curvar->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp; + prefix_eq = (curvar->flag & LVAR_INCASE) ? ltree_prefix_eq_ci : ltree_prefix_eq; if (curvar->flag & LVAR_SUBLEXEME) { - if (compare_subnode(curt, curvar->name, curvar->len, cmpptr, + if (compare_subnode(curt, curvar->name, curvar->len, prefix_eq, (curvar->flag & LVAR_ANYEND))) return success; } else if ((curvar->len == curt->len || (curt->len > curvar->len && (curvar->flag & LVAR_ANYEND))) && - (*cmpptr) (curvar->name, curt->name, curvar->len) == 0) + (*prefix_eq) (curvar->name, curvar->len, curt->name, curt->len)) return success; curvar = LVAR_NEXT(curvar); diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h index 5e0761641d3..78478dec173 100644 --- a/contrib/ltree/ltree.h +++ b/contrib/ltree/ltree.h @@ -157,6 +157,8 @@ typedef struct char data[FLEXIBLE_ARRAY_MEMBER]; } ltxtquery; +typedef bool (*ltree_prefix_eq_func) (const char *, size_t, const char *, size_t); + #define HDRSIZEQT MAXALIGN(VARHDRSZ + sizeof(int32)) #define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + (size) * sizeof(ITEM) + (lenofoperand) ) #define LTXTQUERY_TOO_BIG(size,lenofoperand) \ @@ -208,9 +210,10 @@ bool ltree_execute(ITEM *curitem, void *checkval, int ltree_compare(const ltree *a, const ltree *b); bool inner_isparent(const ltree *c, const ltree *p); bool compare_subnode(ltree_level *t, char *qn, int len, - int (*cmpptr) (const char *, const char *, size_t), bool anyend); + ltree_prefix_eq_func prefix_eq, bool anyend); ltree *lca_inner(ltree **a, int len); -int ltree_strncasecmp(const char *a, const char *b, size_t s); +bool ltree_prefix_eq(const char *a, size_t a_sz, const char *b, size_t b_sz); +bool ltree_prefix_eq_ci(const char *a, size_t a_sz, const char *b, size_t b_sz); /* fmgr macros for ltree objects */ #define DatumGetLtreeP(X) ((ltree *) PG_DETOAST_DATUM(X)) diff --git a/contrib/ltree/ltxtquery_op.c b/contrib/ltree/ltxtquery_op.c index 002102c9c75..3dcbab2c484 100644 --- a/contrib/ltree/ltxtquery_op.c +++ b/contrib/ltree/ltxtquery_op.c @@ -58,19 +58,19 @@ checkcondition_str(void *checkval, ITEM *val) ltree_level *level = LTREE_FIRST(((CHKVAL *) checkval)->node); int tlen = ((CHKVAL *) checkval)->node->numlevel; char *op = ((CHKVAL *) checkval)->operand + val->distance; - int (*cmpptr) (const char *, const char *, size_t); + ltree_prefix_eq_func prefix_eq; - cmpptr = (val->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp; + prefix_eq = (val->flag & LVAR_INCASE) ? ltree_prefix_eq_ci : ltree_prefix_eq; while (tlen > 0) { if (val->flag & LVAR_SUBLEXEME) { - if (compare_subnode(level, op, val->length, cmpptr, (val->flag & LVAR_ANYEND))) + if (compare_subnode(level, op, val->length, prefix_eq, (val->flag & LVAR_ANYEND))) return true; } else if ((val->length == level->len || (level->len > val->length && (val->flag & LVAR_ANYEND))) && - (*cmpptr) (op, level->name, val->length) == 0) + (*prefix_eq) (op, val->length, level->name, level->len)) return true; tlen--; diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml index ee86e170055..423f691dd42 100644 --- a/doc/src/sgml/gist.sgml +++ b/doc/src/sgml/gist.sgml @@ -291,7 +291,7 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops); speed up building a <acronym>GiST</acronym> index. The optional twelfth method <function>stratnum</function> is used to translate compare types (from - <filename>src/include/nodes/primnodes.h</filename>) into strategy numbers + <filename>src/include/access/cmptype.h</filename>) into strategy numbers used by the operator class. This lets the core code look up operators for temporal constraint indexes. </para> @@ -1174,7 +1174,7 @@ my_sortsupport(PG_FUNCTION_ARGS) <listitem> <para> Given a <literal>CompareType</literal> value from - <filename>src/include/nodes/primnodes.h</filename>, returns a strategy + <filename>src/include/access/cmptype.h</filename>, returns a strategy number used by this operator class for matching functionality. The function should return <literal>InvalidStrategy</literal> if the operator class has no matching strategy. diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c index 94db1ec3012..5e6d21969e1 100644 --- a/src/backend/access/transam/parallel.c +++ b/src/backend/access/transam/parallel.c @@ -507,8 +507,12 @@ InitializeParallelDSM(ParallelContext *pcxt) void ReinitializeParallelDSM(ParallelContext *pcxt) { + MemoryContext oldcontext; FixedParallelState *fps; + /* We might be running in a very short-lived memory context. */ + oldcontext = MemoryContextSwitchTo(TopTransactionContext); + /* Wait for any old workers to exit. */ if (pcxt->nworkers_launched > 0) { @@ -546,6 +550,9 @@ ReinitializeParallelDSM(ParallelContext *pcxt) pcxt->worker[i].error_mqh = shm_mq_attach(mq, pcxt->seg, NULL); } } + + /* Restore previous memory context. */ + MemoryContextSwitchTo(oldcontext); } /* diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index 953e185f92d..3a758256591 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -24,6 +24,20 @@ /* use for libc locale names */ #define LOCALE_NAME_BUFLEN 128 +/* + * Maximum number of bytes needed to map a single codepoint. Useful for + * mapping and processing a single input codepoint at a time with a + * statically-allocated buffer. + * + * With full case mapping, an input codepoint may be mapped to as many as + * three output codepoints. See Unicode 16.0.0, section 5.18.2, "Change in + * Length": + * + * https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-5/#G29675 + */ +#define UNICODE_CASEMAP_LEN 3 +#define UNICODE_CASEMAP_BUFSZ (UNICODE_CASEMAP_LEN * MAX_MULTIBYTE_CHAR_LEN) + /* GUC settings */ extern PGDLLIMPORT char *locale_messages; extern PGDLLIMPORT char *locale_monetary; |
