diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index a818023..e4fd0f3 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -440,6 +440,7 @@ PGLC_localeconv(void) static struct lconv CurrentLocaleConv; static bool CurrentLocaleConvAllocated = false; struct lconv *extlconv; + struct lconv tmplconv; char *save_lc_monetary; char *save_lc_numeric; char *decimal_point; @@ -523,23 +524,22 @@ PGLC_localeconv(void) encoding = pg_get_encoding_from_locale(locale_monetary, true); /* - * Must copy all values since restoring internal settings may overwrite - * localeconv()'s results. Note that if we were to fail within this - * sequence before reaching "CurrentLocaleConvAllocated = true", we could - * leak some memory --- but not much, so it's not worth agonizing over. + * First copy all values into a temporary variable since restoring internal + * settings may overwrite localeconv()'s results. Note that elog() cannot + * be called as the backend's locale settings prevail and they are not + * restored yet. */ - CurrentLocaleConv = *extlconv; - CurrentLocaleConv.decimal_point = decimal_point; - CurrentLocaleConv.grouping = grouping; - CurrentLocaleConv.thousands_sep = thousands_sep; - CurrentLocaleConv.int_curr_symbol = db_encoding_strdup(encoding, extlconv->int_curr_symbol); - CurrentLocaleConv.currency_symbol = db_encoding_strdup(encoding, extlconv->currency_symbol); - CurrentLocaleConv.mon_decimal_point = db_encoding_strdup(encoding, extlconv->mon_decimal_point); - CurrentLocaleConv.mon_grouping = strdup(extlconv->mon_grouping); - CurrentLocaleConv.mon_thousands_sep = db_encoding_strdup(encoding, extlconv->mon_thousands_sep); - CurrentLocaleConv.negative_sign = db_encoding_strdup(encoding, extlconv->negative_sign); - CurrentLocaleConv.positive_sign = db_encoding_strdup(encoding, extlconv->positive_sign); - CurrentLocaleConvAllocated = true; + tmplconv = *extlconv; + tmplconv.decimal_point = decimal_point; + tmplconv.grouping = grouping; + tmplconv.thousands_sep = thousands_sep; + tmplconv.int_curr_symbol = db_encoding_strdup(encoding, extlconv->int_curr_symbol); + tmplconv.currency_symbol = db_encoding_strdup(encoding, extlconv->currency_symbol); + tmplconv.mon_decimal_point = db_encoding_strdup(encoding, extlconv->mon_decimal_point); + tmplconv.mon_grouping = strdup(extlconv->mon_grouping); + tmplconv.mon_thousands_sep = db_encoding_strdup(encoding, extlconv->mon_thousands_sep); + tmplconv.negative_sign = db_encoding_strdup(encoding, extlconv->negative_sign); + tmplconv.positive_sign = db_encoding_strdup(encoding, extlconv->positive_sign); /* Try to restore internal settings */ if (save_lc_monetary) @@ -566,6 +566,31 @@ PGLC_localeconv(void) } #endif + /* + * Now that locales are set back to normal check for any NULL fields and + * report an out-of-memory error in consequence if any. + */ + if (tmplconv.decimal_point == NULL || + tmplconv.grouping == NULL || + tmplconv.thousands_sep == NULL || + tmplconv.int_curr_symbol == NULL || + tmplconv.currency_symbol == NULL || + tmplconv.mon_decimal_point == NULL || + tmplconv.mon_grouping == NULL || + tmplconv.mon_thousands_sep == NULL || + tmplconv.negative_sign == NULL || + tmplconv.positive_sign == NULL) + { + free_struct_lconv(&tmplconv); + elog(ERROR, "out of memory"); + } + + /* + * Everything is good, so save the result. + */ + CurrentLocaleConv = tmplconv; + + CurrentLocaleConvAllocated = true; CurrentLocaleConvValid = true; return &CurrentLocaleConv; }