=== Applying patches on top of PostgreSQL commit ID 53a49365052026907afff7613929710d1e7f0da0 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Sat Feb 1 05:27:23 UTC 2025 On branch cf/5141 nothing to commit, working tree clean === applying patch ./v1-0001-Add-datctypeversion-and-collctypeversion.patch Applied patch to 'src/backend/catalog/pg_collation.c' cleanly. Applied patch to 'src/backend/commands/collationcmds.c' cleanly. Applied patch to 'src/backend/commands/dbcommands.c' with conflicts. Applied patch to 'src/backend/utils/adt/pg_locale.c' with conflicts. Applied patch to 'src/bin/initdb/initdb.c' cleanly. Applied patch to 'src/include/catalog/pg_collation.h' cleanly. Applied patch to 'src/include/catalog/pg_database.h' cleanly. Applied patch to 'src/include/catalog/pg_proc.dat' cleanly. Applied patch to 'src/include/utils/pg_locale.h' with conflicts. U src/backend/commands/dbcommands.c U src/backend/utils/adt/pg_locale.c U src/include/utils/pg_locale.h diff --cc src/backend/commands/dbcommands.c index 46310add45,b8bcfd9f78..0000000000 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@@ -705,22 -706,23 +707,42 @@@ createdb(ParseState *pstate, const Crea Oid dboid = InvalidOid; Oid datdba; ListCell *option; ++<<<<<<< ours + DefElem *tablespacenameEl = NULL; + DefElem *ownerEl = NULL; + DefElem *templateEl = NULL; + DefElem *encodingEl = NULL; + DefElem *localeEl = NULL; + DefElem *builtinlocaleEl = NULL; + DefElem *collateEl = NULL; + DefElem *ctypeEl = NULL; + DefElem *iculocaleEl = NULL; + DefElem *icurulesEl = NULL; + DefElem *locproviderEl = NULL; + DefElem *istemplateEl = NULL; + DefElem *allowconnectionsEl = NULL; + DefElem *connlimitEl = NULL; + DefElem *collversionEl = NULL; + DefElem *strategyEl = NULL; ++======= + DefElem *dtablespacename = NULL; + DefElem *downer = NULL; + DefElem *dtemplate = NULL; + DefElem *dencoding = NULL; + DefElem *dlocale = NULL; + DefElem *dbuiltinlocale = NULL; + DefElem *dcollate = NULL; + DefElem *dctype = NULL; + DefElem *diculocale = NULL; + DefElem *dicurules = NULL; + DefElem *dlocprovider = NULL; + DefElem *distemplate = NULL; + DefElem *dallowconnections = NULL; + DefElem *dconnlimit = NULL; + DefElem *dcollversion = NULL; + DefElem *dctypeversion = NULL; + DefElem *dstrategy = NULL; ++>>>>>>> theirs char *dbname = stmt->dbname; char *dbowner = NULL; const char *dbtemplate = NULL; @@@ -831,10 -834,16 +854,16 @@@ } else if (strcmp(defel->defname, "collation_version") == 0) { - if (dcollversion) + if (collversionEl) errorConflictingDefElem(defel, pstate); - dcollversion = defel; + collversionEl = defel; } + else if (strcmp(defel->defname, "ctype_version") == 0) + { + if (dctypeversion) + errorConflictingDefElem(defel, pstate); + dctypeversion = defel; + } else if (strcmp(defel->defname, "location") == 0) { ereport(WARNING, @@@ -956,8 -965,10 +985,15 @@@ (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid connection limit: %d", dbconnlimit))); } ++<<<<<<< ours + if (collversionEl) + dbcollversion = defGetString(collversionEl); ++======= + if (dcollversion) + dbcollversion = defGetString(dcollversion); + if (dctypeversion) + dbctypeversion = defGetString(dctypeversion); ++>>>>>>> theirs /* obtain OID of proposed owner */ if (dbowner) @@@ -1289,8 -1302,25 +1327,25 @@@ dbcollversion = get_collation_actual_version(dblocprovider, locale); } + /* + * Normally, we copy the ctype version from the template database. + * This last resort only applies if the template database does not have a + * ctype version, which is normally only the case for template0. + */ + if (dbctypeversion == NULL) + { + const char *locale; + + if (dblocprovider == COLLPROVIDER_LIBC) + locale = dbctype; + else + locale = dblocale; + + dbctypeversion = get_ctype_actual_version(dblocprovider, locale); + } + /* Resolve default tablespace for new database */ - if (dtablespacename && dtablespacename->arg) + if (tablespacenameEl && tablespacenameEl->arg) { char *tablespacename; AclResult aclresult; @@@ -2544,10 -2567,12 +2606,13 @@@ AlterDatabaseRefreshColl(AlterDatabaseR if (!object_ownercheck(DatabaseRelationId, db_id, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE, stmt->dbname); + LockTuple(rel, &tuple->t_self, InplaceUpdateTupleLock); datum = heap_getattr(tuple, Anum_pg_database_datcollversion, RelationGetDescr(rel), &isnull); - oldversion = isnull ? NULL : TextDatumGetCString(datum); + oldcollversion = isnull ? NULL : TextDatumGetCString(datum); + + datum = heap_getattr(tuple, Anum_pg_database_datctypeversion, RelationGetDescr(rel), &isnull); + oldctypeversion = isnull ? NULL : TextDatumGetCString(datum); if (datForm->datlocprovider == COLLPROVIDER_LIBC) { @@@ -2573,24 -2617,44 +2657,53 @@@ bool nulls[Natts_pg_database] = {0}; bool replaces[Natts_pg_database] = {0}; Datum values[Natts_pg_database] = {0}; + HeapTuple newtuple; ereport(NOTICE, - (errmsg("changing version from %s to %s", - oldversion, newversion))); + (errmsg("changing collation version from %s to %s", + oldcollversion, newcollversion))); - values[Anum_pg_database_datcollversion - 1] = CStringGetTextDatum(newversion); + values[Anum_pg_database_datcollversion - 1] = CStringGetTextDatum(newcollversion); replaces[Anum_pg_database_datcollversion - 1] = true; ++<<<<<<< ours + newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), + values, nulls, replaces); + CatalogTupleUpdate(rel, &tuple->t_self, newtuple); + heap_freetuple(newtuple); ++======= + tuple = heap_modify_tuple(tuple, RelationGetDescr(rel), + values, nulls, replaces); + CatalogTupleUpdate(rel, &tuple->t_self, tuple); + heap_freetuple(tuple); + changed = true; ++>>>>>>> theirs } - else + + if (oldctypeversion && newctypeversion && strcmp(newctypeversion, oldctypeversion) != 0) + { + bool nulls[Natts_pg_database] = {0}; + bool replaces[Natts_pg_database] = {0}; + Datum values[Natts_pg_database] = {0}; + + ereport(NOTICE, + (errmsg("changing ctype version from %s to %s", + oldctypeversion, newctypeversion))); + + values[Anum_pg_database_datctypeversion - 1] = CStringGetTextDatum(newctypeversion); + replaces[Anum_pg_database_datctypeversion - 1] = true; + + tuple = heap_modify_tuple(tuple, RelationGetDescr(rel), + values, nulls, replaces); + CatalogTupleUpdate(rel, &tuple->t_self, tuple); + heap_freetuple(tuple); + changed = true; + } + + if (!changed) ereport(NOTICE, (errmsg("version has not changed"))); + UnlockTuple(rel, &tuple->t_self, InplaceUpdateTupleLock); InvokeObjectPostAlterHook(DatabaseRelationId, db_id, 0); diff --cc src/backend/utils/adt/pg_locale.c index 7d92f580a5,ddcec0b709..0000000000 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@@ -56,9 -56,7 +56,13 @@@ #include "access/htup_details.h" #include "catalog/pg_collation.h" ++<<<<<<< ours +#include "catalog/pg_database.h" +#include "common/hashfn.h" +#include "common/string.h" ++======= + #include "common/unicode_version.h" ++>>>>>>> theirs #include "mb/pg_wchar.h" #include "miscadmin.h" #include "utils/builtins.h" @@@ -1394,83 -1830,296 +1398,133 @@@ get_collation_actual_version(char collp return collversion; } ++<<<<<<< ours +size_t +pg_strlower(char *dst, size_t dstsize, const char *src, ssize_t srclen, + pg_locale_t locale) ++======= + /* + * Get provider-specific ctype version. + */ + char * + get_ctype_actual_version(char collprovider, const char *collctype) + { + if (collprovider == COLLPROVIDER_BUILTIN) + { + if (strcmp(collctype, "C") == 0) + return "1"; + else if (strcmp(collctype, "C.UTF-8") == 0) + return PG_UNICODE_VERSION; + else + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("invalid locale name \"%s\" for builtin provider", + collctype))); + } + else if (collprovider == COLLPROVIDER_ICU) + { + #ifdef USE_ICU + return U_UNICODE_VERSION; + #else + /* could get here if a collation was created by a build with ICU */ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("ICU is not supported in this build"))); + #endif + } + else if (collprovider == COLLPROVIDER_LIBC) + { + return get_collation_actual_version(collprovider, collctype); + } + + return NULL; + } + + /* + * pg_strncoll_libc_win32_utf8 + * + * Win32 does not have UTF-8. Convert UTF8 arguments to wide characters and + * invoke wcscoll() or wcscoll_l(). + */ + #ifdef WIN32 + static int + pg_strncoll_libc_win32_utf8(const char *arg1, size_t len1, const char *arg2, + size_t len2, pg_locale_t locale) ++>>>>>>> theirs { - char sbuf[TEXTBUFLEN]; - char *buf = sbuf; - char *a1p, - *a2p; - int a1len = len1 * 2 + 2; - int a2len = len2 * 2 + 2; - int r; - int result; - - Assert(!locale || locale->provider == COLLPROVIDER_LIBC); - Assert(GetDatabaseEncoding() == PG_UTF8); -#ifndef WIN32 - Assert(false); + if (locale->provider == COLLPROVIDER_BUILTIN) + return strlower_builtin(dst, dstsize, src, srclen, locale); +#ifdef USE_ICU + else if (locale->provider == COLLPROVIDER_ICU) + return strlower_icu(dst, dstsize, src, srclen, locale); #endif - - if (a1len + a2len > TEXTBUFLEN) - buf = palloc(a1len + a2len); - - a1p = buf; - a2p = buf + a1len; - - /* API does not work for zero-length input */ - if (len1 == 0) - r = 0; - else - { - r = MultiByteToWideChar(CP_UTF8, 0, arg1, len1, - (LPWSTR) a1p, a1len / 2); - if (!r) - ereport(ERROR, - (errmsg("could not convert string to UTF-16: error code %lu", - GetLastError()))); - } - ((LPWSTR) a1p)[r] = 0; - - if (len2 == 0) - r = 0; - else - { - r = MultiByteToWideChar(CP_UTF8, 0, arg2, len2, - (LPWSTR) a2p, a2len / 2); - if (!r) - ereport(ERROR, - (errmsg("could not convert string to UTF-16: error code %lu", - GetLastError()))); - } - ((LPWSTR) a2p)[r] = 0; - - errno = 0; - if (locale) - result = wcscoll_l((LPWSTR) a1p, (LPWSTR) a2p, locale->info.lt); + else if (locale->provider == COLLPROVIDER_LIBC) + return strlower_libc(dst, dstsize, src, srclen, locale); else - result = wcscoll((LPWSTR) a1p, (LPWSTR) a2p); - if (result == 2147483647) /* _NLSCMPERROR; missing from mingw headers */ - ereport(ERROR, - (errmsg("could not compare Unicode strings: %m"))); - - if (buf != sbuf) - pfree(buf); + /* shouldn't happen */ + PGLOCALE_SUPPORT_ERROR(locale->provider); - return result; + return 0; /* keep compiler quiet */ } -#endif /* WIN32 */ -/* - * pg_strcoll_libc - * - * Call strcoll(), strcoll_l(), wcscoll(), or wcscoll_l() as appropriate for - * the given locale, platform, and database encoding. If the locale is NULL, - * use the database collation. - * - * Arguments must be encoded in the database encoding and nul-terminated. - */ -static int -pg_strcoll_libc(const char *arg1, const char *arg2, pg_locale_t locale) +size_t +pg_strtitle(char *dst, size_t dstsize, const char *src, ssize_t srclen, + pg_locale_t locale) { - int result; - - Assert(!locale || locale->provider == COLLPROVIDER_LIBC); -#ifdef WIN32 - if (GetDatabaseEncoding() == PG_UTF8) - { - size_t len1 = strlen(arg1); - size_t len2 = strlen(arg2); - - result = pg_strncoll_libc_win32_utf8(arg1, len1, arg2, len2, locale); - } - else -#endif /* WIN32 */ - if (locale) - result = strcoll_l(arg1, arg2, locale->info.lt); + if (locale->provider == COLLPROVIDER_BUILTIN) + return strtitle_builtin(dst, dstsize, src, srclen, locale); +#ifdef USE_ICU + else if (locale->provider == COLLPROVIDER_ICU) + return strtitle_icu(dst, dstsize, src, srclen, locale); +#endif + else if (locale->provider == COLLPROVIDER_LIBC) + return strtitle_libc(dst, dstsize, src, srclen, locale); else - result = strcoll(arg1, arg2); + /* shouldn't happen */ + PGLOCALE_SUPPORT_ERROR(locale->provider); - return result; + return 0; /* keep compiler quiet */ } -/* - * pg_strncoll_libc - * - * Nul-terminate the arguments and call pg_strcoll_libc(). - */ -static int -pg_strncoll_libc(const char *arg1, size_t len1, const char *arg2, size_t len2, - pg_locale_t locale) +size_t +pg_strupper(char *dst, size_t dstsize, const char *src, ssize_t srclen, + pg_locale_t locale) { - char sbuf[TEXTBUFLEN]; - char *buf = sbuf; - size_t bufsize1 = len1 + 1; - size_t bufsize2 = len2 + 1; - char *arg1n; - char *arg2n; - int result; - - Assert(!locale || locale->provider == COLLPROVIDER_LIBC); - -#ifdef WIN32 - /* check for this case before doing the work for nul-termination */ - if (GetDatabaseEncoding() == PG_UTF8) - return pg_strncoll_libc_win32_utf8(arg1, len1, arg2, len2, locale); -#endif /* WIN32 */ - - if (bufsize1 + bufsize2 > TEXTBUFLEN) - buf = palloc(bufsize1 + bufsize2); - - arg1n = buf; - arg2n = buf + bufsize1; - - /* nul-terminate arguments */ - memcpy(arg1n, arg1, len1); - arg1n[len1] = '\0'; - memcpy(arg2n, arg2, len2); - arg2n[len2] = '\0'; - - result = pg_strcoll_libc(arg1n, arg2n, locale); - - if (buf != sbuf) - pfree(buf); - - return result; -} - + if (locale->provider == COLLPROVIDER_BUILTIN) + return strupper_builtin(dst, dstsize, src, srclen, locale); #ifdef USE_ICU - -/* - * pg_strncoll_icu_no_utf8 - * - * Convert the arguments from the database encoding to UChar strings, then - * call ucol_strcoll(). An argument length of -1 means that the string is - * NUL-terminated. - * - * When the database encoding is UTF-8, and ICU supports ucol_strcollUTF8(), - * caller should call that instead. - */ -static int -pg_strncoll_icu_no_utf8(const char *arg1, int32_t len1, - const char *arg2, int32_t len2, pg_locale_t locale) -{ - char sbuf[TEXTBUFLEN]; - char *buf = sbuf; - int32_t ulen1; - int32_t ulen2; - size_t bufsize1; - size_t bufsize2; - UChar *uchar1, - *uchar2; - int result; - - Assert(locale->provider == COLLPROVIDER_ICU); -#ifdef HAVE_UCOL_STRCOLLUTF8 - Assert(GetDatabaseEncoding() != PG_UTF8); + else if (locale->provider == COLLPROVIDER_ICU) + return strupper_icu(dst, dstsize, src, srclen, locale); #endif + else if (locale->provider == COLLPROVIDER_LIBC) + return strupper_libc(dst, dstsize, src, srclen, locale); + else + /* shouldn't happen */ + PGLOCALE_SUPPORT_ERROR(locale->provider); - init_icu_converter(); - - ulen1 = uchar_length(icu_converter, arg1, len1); - ulen2 = uchar_length(icu_converter, arg2, len2); - - bufsize1 = (ulen1 + 1) * sizeof(UChar); - bufsize2 = (ulen2 + 1) * sizeof(UChar); - - if (bufsize1 + bufsize2 > TEXTBUFLEN) - buf = palloc(bufsize1 + bufsize2); - - uchar1 = (UChar *) buf; - uchar2 = (UChar *) (buf + bufsize1); - - ulen1 = uchar_convert(icu_converter, uchar1, ulen1 + 1, arg1, len1); - ulen2 = uchar_convert(icu_converter, uchar2, ulen2 + 1, arg2, len2); - - result = ucol_strcoll(locale->info.icu.ucol, - uchar1, ulen1, - uchar2, ulen2); - - if (buf != sbuf) - pfree(buf); - - return result; + return 0; /* keep compiler quiet */ } -/* - * pg_strncoll_icu - * - * Call ucol_strcollUTF8() or ucol_strcoll() as appropriate for the given - * database encoding. An argument length of -1 means the string is - * NUL-terminated. - * - * Arguments must be encoded in the database encoding. - */ -static int -pg_strncoll_icu(const char *arg1, int32_t len1, const char *arg2, int32_t len2, - pg_locale_t locale) +size_t +pg_strfold(char *dst, size_t dstsize, const char *src, ssize_t srclen, + pg_locale_t locale) { - int result; - - Assert(locale->provider == COLLPROVIDER_ICU); - -#ifdef HAVE_UCOL_STRCOLLUTF8 - if (GetDatabaseEncoding() == PG_UTF8) - { - UErrorCode status; - - status = U_ZERO_ERROR; - result = ucol_strcollUTF8(locale->info.icu.ucol, - arg1, len1, - arg2, len2, - &status); - if (U_FAILURE(status)) - ereport(ERROR, - (errmsg("collation failed: %s", u_errorName(status)))); - } - else + if (locale->provider == COLLPROVIDER_BUILTIN) + return strfold_builtin(dst, dstsize, src, srclen, locale); +#ifdef USE_ICU + else if (locale->provider == COLLPROVIDER_ICU) + return strfold_icu(dst, dstsize, src, srclen, locale); #endif - { - result = pg_strncoll_icu_no_utf8(arg1, len1, arg2, len2, locale); - } + /* for libc, just use strlower */ + else if (locale->provider == COLLPROVIDER_LIBC) + return strlower_libc(dst, dstsize, src, srclen, locale); + else + /* shouldn't happen */ + PGLOCALE_SUPPORT_ERROR(locale->provider); - return result; + return 0; /* keep compiler quiet */ } -#endif /* USE_ICU */ - /* * pg_strcoll * diff --cc src/include/utils/pg_locale.h index 0d5f0513ce,35cf0b39f4..0000000000 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@@ -125,21 -103,10 +125,25 @@@ extern void init_database_collation(voi extern pg_locale_t pg_newlocale_from_collation(Oid collid); extern char *get_collation_actual_version(char collprovider, const char *collcollate); ++<<<<<<< ours +extern size_t pg_strlower(char *dest, size_t destsize, + const char *src, ssize_t srclen, + pg_locale_t locale); +extern size_t pg_strtitle(char *dest, size_t destsize, + const char *src, ssize_t srclen, + pg_locale_t locale); +extern size_t pg_strupper(char *dest, size_t destsize, + const char *src, ssize_t srclen, + pg_locale_t locale); +extern size_t pg_strfold(char *dest, size_t destsize, + const char *src, ssize_t srclen, + pg_locale_t locale); ++======= + extern char *get_ctype_actual_version(char collprovider, const char *collctype); ++>>>>>>> theirs extern int pg_strcoll(const char *arg1, const char *arg2, pg_locale_t locale); -extern int pg_strncoll(const char *arg1, size_t len1, - const char *arg2, size_t len2, pg_locale_t locale); +extern int pg_strncoll(const char *arg1, ssize_t len1, + const char *arg2, ssize_t len2, pg_locale_t locale); extern bool pg_strxfrm_enabled(pg_locale_t locale); extern size_t pg_strxfrm(char *dest, const char *src, size_t destsize, pg_locale_t locale);