Tighten up make_libc_collator() and make_icu_collator().
Ensure that error paths within these functions do not leak a collator, and return the result rather than using an out parameter. (Error paths in the caller may still result in a leaked collator, which will be addressed separately.) In make_libc_collator(), if the first newlocale() succeeds and the second one fails, close the first locale_t object. The function make_icu_collator() doesn't have any external callers, so change it to be static. Discussion: https://postgr.es/m/54d20e812bd6c3e44c10eddcd757ec494ebf1803.camel@j-davis.com
This commit is contained in:
parent
59f0eea7b0
commit
ceeaaed87a
@ -1297,14 +1297,15 @@ report_newlocale_failure(const char *localename)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the locale_t field.
|
* Create a locale_t with the given collation and ctype.
|
||||||
*
|
*
|
||||||
* The "C" and "POSIX" locales are not actually handled by libc, so set the
|
* The "C" and "POSIX" locales are not actually handled by libc, so return
|
||||||
* locale_t to zero in that case.
|
* NULL.
|
||||||
|
*
|
||||||
|
* Ensure that no path leaks a locale_t.
|
||||||
*/
|
*/
|
||||||
static void
|
static locale_t
|
||||||
make_libc_collator(const char *collate, const char *ctype,
|
make_libc_collator(const char *collate, const char *ctype)
|
||||||
pg_locale_t result)
|
|
||||||
{
|
{
|
||||||
locale_t loc = 0;
|
locale_t loc = 0;
|
||||||
|
|
||||||
@ -1343,7 +1344,11 @@ make_libc_collator(const char *collate, const char *ctype,
|
|||||||
errno = 0;
|
errno = 0;
|
||||||
loc = newlocale(LC_CTYPE_MASK, ctype, loc1);
|
loc = newlocale(LC_CTYPE_MASK, ctype, loc1);
|
||||||
if (!loc)
|
if (!loc)
|
||||||
|
{
|
||||||
|
if (loc1)
|
||||||
|
freelocale(loc1);
|
||||||
report_newlocale_failure(ctype);
|
report_newlocale_failure(ctype);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
loc = loc1;
|
loc = loc1;
|
||||||
@ -1360,60 +1365,78 @@ make_libc_collator(const char *collate, const char *ctype,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
result->info.lt = loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
/*
|
||||||
make_icu_collator(const char *iculocstr,
|
* Create a UCollator with the given locale string and rules.
|
||||||
const char *icurules,
|
*
|
||||||
struct pg_locale_struct *resultp)
|
* Ensure that no path leaks a UCollator.
|
||||||
{
|
*/
|
||||||
#ifdef USE_ICU
|
#ifdef USE_ICU
|
||||||
UCollator *collator;
|
static UCollator *
|
||||||
|
make_icu_collator(const char *iculocstr, const char *icurules)
|
||||||
collator = pg_ucol_open(iculocstr);
|
{
|
||||||
|
if (!icurules)
|
||||||
/*
|
|
||||||
* If rules are specified, we extract the rules of the standard collation,
|
|
||||||
* add our own rules, and make a new collator with the combined rules.
|
|
||||||
*/
|
|
||||||
if (icurules)
|
|
||||||
{
|
{
|
||||||
const UChar *default_rules;
|
/* simple case without rules */
|
||||||
UChar *agg_rules;
|
return pg_ucol_open(iculocstr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UCollator *collator_std_rules;
|
||||||
|
UCollator *collator_all_rules;
|
||||||
|
const UChar *std_rules;
|
||||||
UChar *my_rules;
|
UChar *my_rules;
|
||||||
UErrorCode status;
|
UChar *all_rules;
|
||||||
int32_t length;
|
int32_t length;
|
||||||
|
int32_t total;
|
||||||
|
UErrorCode status;
|
||||||
|
|
||||||
default_rules = ucol_getRules(collator, &length);
|
/*
|
||||||
|
* If rules are specified, we extract the rules of the standard
|
||||||
|
* collation, add our own rules, and make a new collator with the
|
||||||
|
* combined rules.
|
||||||
|
*/
|
||||||
icu_to_uchar(&my_rules, icurules, strlen(icurules));
|
icu_to_uchar(&my_rules, icurules, strlen(icurules));
|
||||||
|
|
||||||
agg_rules = palloc_array(UChar, u_strlen(default_rules) + u_strlen(my_rules) + 1);
|
collator_std_rules = pg_ucol_open(iculocstr);
|
||||||
u_strcpy(agg_rules, default_rules);
|
|
||||||
u_strcat(agg_rules, my_rules);
|
|
||||||
|
|
||||||
ucol_close(collator);
|
std_rules = ucol_getRules(collator_std_rules, &length);
|
||||||
|
|
||||||
|
total = u_strlen(std_rules) + u_strlen(my_rules) + 1;
|
||||||
|
|
||||||
|
/* avoid leaking collator on OOM */
|
||||||
|
all_rules = palloc_extended(sizeof(UChar) * total, MCXT_ALLOC_NO_OOM);
|
||||||
|
if (!all_rules)
|
||||||
|
{
|
||||||
|
ucol_close(collator_std_rules);
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||||
|
errmsg("out of memory")));
|
||||||
|
}
|
||||||
|
|
||||||
|
u_strcpy(all_rules, std_rules);
|
||||||
|
u_strcat(all_rules, my_rules);
|
||||||
|
|
||||||
|
ucol_close(collator_std_rules);
|
||||||
|
|
||||||
status = U_ZERO_ERROR;
|
status = U_ZERO_ERROR;
|
||||||
collator = ucol_openRules(agg_rules, u_strlen(agg_rules),
|
collator_all_rules = ucol_openRules(all_rules, u_strlen(all_rules),
|
||||||
UCOL_DEFAULT, UCOL_DEFAULT_STRENGTH, NULL, &status);
|
UCOL_DEFAULT, UCOL_DEFAULT_STRENGTH,
|
||||||
|
NULL, &status);
|
||||||
if (U_FAILURE(status))
|
if (U_FAILURE(status))
|
||||||
|
{
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("could not open collator for locale \"%s\" with rules \"%s\": %s",
|
errmsg("could not open collator for locale \"%s\" with rules \"%s\": %s",
|
||||||
iculocstr, icurules, u_errorName(status))));
|
iculocstr, icurules, u_errorName(status))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We will leak this string if the caller errors later :-( */
|
return collator_all_rules;
|
||||||
resultp->info.icu.locale = MemoryContextStrdup(TopMemoryContext, iculocstr);
|
}
|
||||||
resultp->info.icu.ucol = collator;
|
|
||||||
#else /* not USE_ICU */
|
|
||||||
/* 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 /* not USE_ICU */
|
|
||||||
}
|
}
|
||||||
|
#endif /* not USE_ICU */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize default_locale with database locale settings.
|
* Initialize default_locale with database locale settings.
|
||||||
@ -1424,7 +1447,6 @@ init_database_collation(void)
|
|||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
Form_pg_database dbform;
|
Form_pg_database dbform;
|
||||||
Datum datum;
|
Datum datum;
|
||||||
bool isnull;
|
|
||||||
|
|
||||||
/* Fetch our pg_database row normally, via syscache */
|
/* Fetch our pg_database row normally, via syscache */
|
||||||
tup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
|
tup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
|
||||||
@ -1449,8 +1471,10 @@ init_database_collation(void)
|
|||||||
}
|
}
|
||||||
else if (dbform->datlocprovider == COLLPROVIDER_ICU)
|
else if (dbform->datlocprovider == COLLPROVIDER_ICU)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_ICU
|
||||||
char *datlocale;
|
char *datlocale;
|
||||||
char *icurules;
|
char *icurules;
|
||||||
|
bool isnull;
|
||||||
|
|
||||||
datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datlocale);
|
datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datlocale);
|
||||||
datlocale = TextDatumGetCString(datum);
|
datlocale = TextDatumGetCString(datum);
|
||||||
@ -1464,15 +1488,20 @@ init_database_collation(void)
|
|||||||
else
|
else
|
||||||
icurules = NULL;
|
icurules = NULL;
|
||||||
|
|
||||||
make_icu_collator(datlocale, icurules, &default_locale);
|
default_locale.info.icu.locale = MemoryContextStrdup(TopMemoryContext, datlocale);
|
||||||
|
default_locale.info.icu.ucol = make_icu_collator(datlocale, icurules);
|
||||||
|
#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
|
else if (dbform->datlocprovider == COLLPROVIDER_LIBC)
|
||||||
{
|
{
|
||||||
const char *datcollate;
|
const char *datcollate;
|
||||||
const char *datctype;
|
const char *datctype;
|
||||||
|
|
||||||
Assert(dbform->datlocprovider == COLLPROVIDER_LIBC);
|
|
||||||
|
|
||||||
datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datcollate);
|
datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datcollate);
|
||||||
datcollate = TextDatumGetCString(datum);
|
datcollate = TextDatumGetCString(datum);
|
||||||
datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datctype);
|
datum = SysCacheGetAttrNotNull(DATABASEOID, tup, Anum_pg_database_datctype);
|
||||||
@ -1483,8 +1512,12 @@ init_database_collation(void)
|
|||||||
default_locale.ctype_is_c = (strcmp(datctype, "C") == 0) ||
|
default_locale.ctype_is_c = (strcmp(datctype, "C") == 0) ||
|
||||||
(strcmp(datctype, "POSIX") == 0);
|
(strcmp(datctype, "POSIX") == 0);
|
||||||
|
|
||||||
make_libc_collator(datcollate, datctype, &default_locale);
|
default_locale.info.lt = make_libc_collator(datcollate, datctype);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
/* shouldn't happen */
|
||||||
|
PGLOCALE_SUPPORT_ERROR(dbform->datlocprovider);
|
||||||
|
|
||||||
|
|
||||||
default_locale.provider = dbform->datlocprovider;
|
default_locale.provider = dbform->datlocprovider;
|
||||||
|
|
||||||
@ -1557,25 +1590,9 @@ pg_newlocale_from_collation(Oid collid)
|
|||||||
result.info.builtin.locale = MemoryContextStrdup(TopMemoryContext,
|
result.info.builtin.locale = MemoryContextStrdup(TopMemoryContext,
|
||||||
locstr);
|
locstr);
|
||||||
}
|
}
|
||||||
else if (collform->collprovider == COLLPROVIDER_LIBC)
|
|
||||||
{
|
|
||||||
const char *collcollate;
|
|
||||||
const char *collctype;
|
|
||||||
|
|
||||||
datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collcollate);
|
|
||||||
collcollate = TextDatumGetCString(datum);
|
|
||||||
datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collctype);
|
|
||||||
collctype = TextDatumGetCString(datum);
|
|
||||||
|
|
||||||
result.collate_is_c = (strcmp(collcollate, "C") == 0) ||
|
|
||||||
(strcmp(collcollate, "POSIX") == 0);
|
|
||||||
result.ctype_is_c = (strcmp(collctype, "C") == 0) ||
|
|
||||||
(strcmp(collctype, "POSIX") == 0);
|
|
||||||
|
|
||||||
make_libc_collator(collcollate, collctype, &result);
|
|
||||||
}
|
|
||||||
else if (collform->collprovider == COLLPROVIDER_ICU)
|
else if (collform->collprovider == COLLPROVIDER_ICU)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_ICU
|
||||||
const char *iculocstr;
|
const char *iculocstr;
|
||||||
const char *icurules;
|
const char *icurules;
|
||||||
|
|
||||||
@ -1591,8 +1608,35 @@ pg_newlocale_from_collation(Oid collid)
|
|||||||
else
|
else
|
||||||
icurules = NULL;
|
icurules = NULL;
|
||||||
|
|
||||||
make_icu_collator(iculocstr, icurules, &result);
|
result.info.icu.locale = MemoryContextStrdup(TopMemoryContext, iculocstr);
|
||||||
|
result.info.icu.ucol = make_icu_collator(iculocstr, icurules);
|
||||||
|
#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 (collform->collprovider == COLLPROVIDER_LIBC)
|
||||||
|
{
|
||||||
|
const char *collcollate;
|
||||||
|
const char *collctype;
|
||||||
|
|
||||||
|
datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collcollate);
|
||||||
|
collcollate = TextDatumGetCString(datum);
|
||||||
|
datum = SysCacheGetAttrNotNull(COLLOID, tp, Anum_pg_collation_collctype);
|
||||||
|
collctype = TextDatumGetCString(datum);
|
||||||
|
|
||||||
|
result.collate_is_c = (strcmp(collcollate, "C") == 0) ||
|
||||||
|
(strcmp(collcollate, "POSIX") == 0);
|
||||||
|
result.ctype_is_c = (strcmp(collctype, "C") == 0) ||
|
||||||
|
(strcmp(collctype, "POSIX") == 0);
|
||||||
|
|
||||||
|
result.info.lt = make_libc_collator(collcollate, collctype);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* shouldn't happen */
|
||||||
|
PGLOCALE_SUPPORT_ERROR(collform->collprovider);
|
||||||
|
|
||||||
datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collversion,
|
datum = SysCacheGetAttr(COLLOID, tp, Anum_pg_collation_collversion,
|
||||||
&isnull);
|
&isnull);
|
||||||
@ -2500,6 +2544,8 @@ builtin_validate_locale(int encoding, const char *locale)
|
|||||||
/*
|
/*
|
||||||
* Wrapper around ucol_open() to handle API differences for older ICU
|
* Wrapper around ucol_open() to handle API differences for older ICU
|
||||||
* versions.
|
* versions.
|
||||||
|
*
|
||||||
|
* Ensure that no path leaks a UCollator.
|
||||||
*/
|
*/
|
||||||
static UCollator *
|
static UCollator *
|
||||||
pg_ucol_open(const char *loc_str)
|
pg_ucol_open(const char *loc_str)
|
||||||
|
@ -104,10 +104,6 @@ struct pg_locale_struct
|
|||||||
|
|
||||||
typedef struct pg_locale_struct *pg_locale_t;
|
typedef struct pg_locale_struct *pg_locale_t;
|
||||||
|
|
||||||
extern void make_icu_collator(const char *iculocstr,
|
|
||||||
const char *icurules,
|
|
||||||
struct pg_locale_struct *resultp);
|
|
||||||
|
|
||||||
extern void init_database_collation(void);
|
extern void init_database_collation(void);
|
||||||
extern pg_locale_t pg_newlocale_from_collation(Oid collid);
|
extern pg_locale_t pg_newlocale_from_collation(Oid collid);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user