99#include <access/htup_details.h>
1010#include <catalog/pg_type.h>
1111#include <commands/defrem.h>
12+ #include <nodes/miscnodes.h>
1213#include <nodes/parsenodes.h>
1314#include <utils/builtins.h>
1415#include <utils/lsyscache.h>
@@ -83,19 +84,9 @@ ts_with_clause_definition_names(const WithClauseDefinition *args, Size nargs)
8384 return buf .data ;
8485}
8586
86- /*
87- * Deserialize and apply the values in a WITH clause based on the on_arg table.
88- *
89- * This function will go through every element in def_elems and search for a
90- * corresponding argument in args, if one is found it will attempt to deserialize
91- * the argument, using that table elements deserialize function, then apply it
92- * to state.
93- *
94- * This is used to turn the list into a form more useful for our internal
95- * functions
96- */
97- WithClauseResult *
98- ts_with_clauses_parse (const List * def_elems , const WithClauseDefinition * args , Size nargs )
87+ static WithClauseResult *
88+ with_clauses_parse_internal (const List * def_elems , const WithClauseDefinition * args , Size nargs ,
89+ bool parse_values )
9990{
10091 ListCell * cell ;
10192 WithClauseResult * results = palloc0 (sizeof (* results ) * nargs );
@@ -130,7 +121,11 @@ ts_with_clauses_parse(const List *def_elems, const WithClauseDefinition *args, S
130121 def -> defname )));
131122 }
132123
133- results [i ].parsed = parse_arg (args [i ], def );
124+ /* RESET clauses carry only option names, so skip value parsing */
125+ if (parse_values )
126+ {
127+ results [i ].parsed = parse_arg (args [i ], def );
128+ }
134129 results [i ].is_default = false;
135130 break ;
136131 }
@@ -150,6 +145,23 @@ ts_with_clauses_parse(const List *def_elems, const WithClauseDefinition *args, S
150145 return results ;
151146}
152147
148+ /*
149+ * Deserialize and apply the values in a WITH clause based on the on_arg table.
150+ *
151+ * This function will go through every element in def_elems and search for a
152+ * corresponding argument in args, if one is found it will attempt to deserialize
153+ * the argument, using that table elements deserialize function, then apply it
154+ * to state.
155+ *
156+ * This is used to turn the list into a form more useful for our internal
157+ * functions
158+ */
159+ WithClauseResult *
160+ ts_with_clauses_parse (const List * def_elems , const WithClauseDefinition * args , Size nargs )
161+ {
162+ return with_clauses_parse_internal (def_elems , args , nargs , true);
163+ }
164+
153165/*
154166 * This function handles parsing of WITH clauses for ALTER TABLE RESET.
155167 * Unlike ts_with_clauses_parse, it does not parse any option values,
@@ -158,56 +170,7 @@ ts_with_clauses_parse(const List *def_elems, const WithClauseDefinition *args, S
158170WithClauseResult *
159171ts_with_clauses_parse_reset (const List * def_elems , const WithClauseDefinition * args , Size nargs )
160172{
161- ListCell * cell ;
162- WithClauseResult * results = palloc0 (sizeof (* results ) * nargs );
163- Size i ;
164-
165- for (i = 0 ; i < nargs ; i ++ )
166- {
167- results [i ].definition = & args [i ];
168- results [i ].parsed = args [i ].default_val ;
169- results [i ].is_default = true;
170- }
171-
172- foreach (cell , def_elems )
173- {
174- DefElem * def = (DefElem * ) lfirst (cell );
175- bool argument_recognized = false;
176-
177- for (i = 0 ; i < nargs ; i ++ )
178- {
179- for (int j = 0 ; args [i ].arg_names [j ] != NULL ; ++ j )
180- {
181- if (pg_strcasecmp (def -> defname , args [i ].arg_names [j ]) == 0 )
182- {
183- argument_recognized = true;
184-
185- if (!results [i ].is_default )
186- {
187- ereport (ERROR ,
188- (errcode (ERRCODE_AMBIGUOUS_PARAMETER ),
189- errmsg ("duplicate parameter \"%s.%s\"" ,
190- def -> defnamespace ,
191- def -> defname )));
192- }
193-
194- results [i ].is_default = false;
195- break ;
196- }
197- }
198- }
199-
200- if (!argument_recognized )
201- {
202- ereport (ERROR ,
203- (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
204- errmsg ("unrecognized parameter \"%s.%s\"" , def -> defnamespace , def -> defname ),
205- errhint ("Valid timescaledb parameters are: %s" ,
206- ts_with_clause_definition_names (args , nargs ))));
207- }
208- }
209-
210- return results ;
173+ return with_clauses_parse_internal (def_elems , args , nargs , false);
211174}
212175
213176extern TSDLLEXPORT char *
@@ -227,6 +190,8 @@ parse_arg(WithClauseDefinition arg, DefElem *def)
227190 Datum val ;
228191 Oid in_fn ;
229192 Oid typIOParam ;
193+ FmgrInfo in_fn_info ;
194+ ErrorSaveContext escontext = { .type = T_ErrorSaveContext };
230195
231196 if (!OidIsValid (arg .type_id ))
232197 {
@@ -256,38 +221,14 @@ parse_arg(WithClauseDefinition arg, DefElem *def)
256221
257222 Assert (OidIsValid (in_fn ));
258223
224+ fmgr_info (in_fn , & in_fn_info );
225+
259226 /*
260- * We could use InputFunctionCallSafe() here but this is just supported
261- * for PG16 and later, so we opt for checking if the failure is what we
262- * expected and re-throwing the error otherwise.
227+ * Parse the value with our own error context so we can raise a custom error message for invalid
228+ * input instead of the generic one from the type's input function.
263229 */
264- PG_TRY ();
265- {
266- val = OidInputFunctionCall (in_fn , value , typIOParam , -1 );
267- }
268- PG_CATCH ();
230+ if (!InputFunctionCallSafe (& in_fn_info , value , typIOParam , -1 , (Node * ) & escontext , & val ))
269231 {
270- const int sqlerrcode = geterrcode ();
271- /*
272- * We can deal with the Data Exception category and in the Syntax
273- * Error or Access Rule Violation category, but if the error is an
274- * insufficient resources category, for example, an out of memory
275- * error, we should just re-throw it.
276- *
277- * Errors in other categories are unlikely, but we cannot do anything
278- * with them anyway, so just re-throw them as well.
279- */
280- if (ERRCODE_TO_CATEGORY (sqlerrcode ) != ERRCODE_DATA_EXCEPTION &&
281- ERRCODE_TO_CATEGORY (sqlerrcode ) != ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION )
282- {
283- PG_RE_THROW ();
284- }
285- FlushErrorState ();
286-
287- /* We are currently using the ErrorContext, but since we are going to
288- * raise an error later, there is no reason to switch memory context
289- * nor restore the resource owner here. */
290-
291232 Form_pg_type typetup ;
292233 HeapTuple tup = SearchSysCache1 (TYPEOID , ObjectIdGetDatum (arg .type_id ));
293234 if (!HeapTupleIsValid (tup ))
@@ -309,6 +250,6 @@ parse_arg(WithClauseDefinition arg, DefElem *def)
309250 def -> defname ,
310251 NameStr (typetup -> typname ))));
311252 }
312- PG_END_TRY ();
253+
313254 return val ;
314255}
0 commit comments