1 /*-------------------------------------------------------------------------
2 *
3 * deparse_function_stmts.c
4 *
5 * All routines to deparse function and procedure statements.
6 * This file contains all entry points specific for function and procedure statement
7 * deparsing
8 *
9 * Functions that could move later are AppendDefElem, AppendDefElemStrict, etc. These
10 * should be reused across multiple statements and should live in their own deparse
11 * file.
12 *
13 * Copyright (c), Citus Data, Inc.
14 *
15 *-------------------------------------------------------------------------
16 */
17
18 #include "postgres.h"
19
20 #include "access/htup_details.h"
21 #include "catalog/namespace.h"
22 #include "catalog/pg_proc.h"
23 #include "catalog/pg_type.h"
24 #include "commands/defrem.h"
25 #include "distributed/citus_ruleutils.h"
26 #include "distributed/commands.h"
27 #include "distributed/deparser.h"
28 #include "distributed/version_compat.h"
29 #include "lib/stringinfo.h"
30 #include "nodes/makefuncs.h"
31 #include "nodes/nodes.h"
32 #include "nodes/value.h"
33 #include "parser/parse_func.h"
34 #include "parser/parse_type.h"
35 #include "utils/builtins.h"
36 #include "utils/fmgroids.h"
37 #include "utils/fmgrprotos.h"
38 #include "utils/guc.h"
39 #include "utils/lsyscache.h"
40 #include "utils/memutils.h"
41 #include "utils/syscache.h"
42 #include "utils/regproc.h"
43
44
45 /* forward declaration for deparse functions */
46 static char * ObjectTypeToKeyword(ObjectType objtype);
47
48 static void AppendAlterFunctionStmt(StringInfo buf, AlterFunctionStmt *stmt);
49 static void AppendDropFunctionStmt(StringInfo buf, DropStmt *stmt);
50 static void AppendFunctionName(StringInfo buf, ObjectWithArgs *func, ObjectType objtype);
51 static void AppendFunctionNameList(StringInfo buf, List *objects, ObjectType objtype);
52
53 static void AppendDefElem(StringInfo buf, DefElem *def);
54 static void AppendDefElemStrict(StringInfo buf, DefElem *def);
55 static void AppendDefElemVolatility(StringInfo buf, DefElem *def);
56 static void AppendDefElemLeakproof(StringInfo buf, DefElem *def);
57 static void AppendDefElemSecurity(StringInfo buf, DefElem *def);
58 static void AppendDefElemParallel(StringInfo buf, DefElem *def);
59 static void AppendDefElemCost(StringInfo buf, DefElem *def);
60 static void AppendDefElemRows(StringInfo buf, DefElem *def);
61 static void AppendDefElemSet(StringInfo buf, DefElem *def);
62
63 static void AppendVarSetValue(StringInfo buf, VariableSetStmt *setStmt);
64 static void AppendRenameFunctionStmt(StringInfo buf, RenameStmt *stmt);
65 static void AppendAlterFunctionSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);
66 static void AppendAlterFunctionOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);
67 static void AppendAlterFunctionDependsStmt(StringInfo buf, AlterObjectDependsStmt *stmt);
68
69 static char * CopyAndConvertToUpperCase(const char *str);
70
71 /*
72 * DeparseAlterFunctionStmt builds and returns a string representing the AlterFunctionStmt
73 */
74 char *
DeparseAlterFunctionStmt(Node * node)75 DeparseAlterFunctionStmt(Node *node)
76 {
77 AlterFunctionStmt *stmt = castNode(AlterFunctionStmt, node);
78 StringInfoData str = { 0 };
79 initStringInfo(&str);
80
81 AppendAlterFunctionStmt(&str, stmt);
82
83 return str.data;
84 }
85
86
87 /*
88 * ObjectTypeToKeyword returns an appropriate string for the given ObjectType
89 * Where the string will be one of "FUNCTION", "PROCEDURE", or "AGGREGATE"
90 */
91 static char *
ObjectTypeToKeyword(ObjectType objtype)92 ObjectTypeToKeyword(ObjectType objtype)
93 {
94 switch (objtype)
95 {
96 case OBJECT_FUNCTION:
97 {
98 return "FUNCTION";
99 }
100
101 case OBJECT_PROCEDURE:
102 {
103 return "PROCEDURE";
104 }
105
106 case OBJECT_AGGREGATE:
107 {
108 return "AGGREGATE";
109 }
110
111 case OBJECT_ROUTINE:
112 {
113 return "ROUTINE";
114 }
115
116 default:
117 elog(ERROR, "Unknown object type: %d", objtype);
118 return NULL;
119 }
120 }
121
122
123 /*
124 * AppendAlterFunctionStmt appends a string representing the AlterFunctionStmt to a buffer
125 */
126 static void
AppendAlterFunctionStmt(StringInfo buf,AlterFunctionStmt * stmt)127 AppendAlterFunctionStmt(StringInfo buf, AlterFunctionStmt *stmt)
128 {
129 ListCell *actionCell = NULL;
130
131 appendStringInfo(buf, "ALTER %s ", ObjectTypeToKeyword(stmt->objtype));
132 AppendFunctionName(buf, stmt->func, stmt->objtype);
133
134 foreach(actionCell, stmt->actions)
135 {
136 DefElem *def = castNode(DefElem, lfirst(actionCell));
137 AppendDefElem(buf, def);
138 }
139
140 appendStringInfoString(buf, ";");
141 }
142
143
144 /*
145 * AppendDefElem appends a string representing the DefElem to a buffer
146 */
147 static void
AppendDefElem(StringInfo buf,DefElem * def)148 AppendDefElem(StringInfo buf, DefElem *def)
149 {
150 if (strcmp(def->defname, "strict") == 0)
151 {
152 AppendDefElemStrict(buf, def);
153 }
154 else if (strcmp(def->defname, "volatility") == 0)
155 {
156 AppendDefElemVolatility(buf, def);
157 }
158 else if (strcmp(def->defname, "leakproof") == 0)
159 {
160 AppendDefElemLeakproof(buf, def);
161 }
162 else if (strcmp(def->defname, "security") == 0)
163 {
164 AppendDefElemSecurity(buf, def);
165 }
166 else if (strcmp(def->defname, "parallel") == 0)
167 {
168 AppendDefElemParallel(buf, def);
169 }
170 else if (strcmp(def->defname, "cost") == 0)
171 {
172 AppendDefElemCost(buf, def);
173 }
174 else if (strcmp(def->defname, "rows") == 0)
175 {
176 AppendDefElemRows(buf, def);
177 }
178 else if (strcmp(def->defname, "set") == 0)
179 {
180 AppendDefElemSet(buf, def);
181 }
182 }
183
184
185 /*
186 * AppendDefElemStrict appends a string representing the DefElem to a buffer
187 */
188 static void
AppendDefElemStrict(StringInfo buf,DefElem * def)189 AppendDefElemStrict(StringInfo buf, DefElem *def)
190 {
191 if (intVal(def->arg) == 1)
192 {
193 appendStringInfo(buf, " STRICT");
194 }
195 else
196 {
197 appendStringInfo(buf, " CALLED ON NULL INPUT");
198 }
199 }
200
201
202 /*
203 * AppendDefElemVolatility appends a string representing the DefElem to a buffer
204 */
205 static void
AppendDefElemVolatility(StringInfo buf,DefElem * def)206 AppendDefElemVolatility(StringInfo buf, DefElem *def)
207 {
208 appendStringInfo(buf, " %s", CopyAndConvertToUpperCase(strVal(def->arg)));
209 }
210
211
212 /*
213 * AppendDefElemLeakproof appends a string representing the DefElem to a buffer
214 */
215 static void
AppendDefElemLeakproof(StringInfo buf,DefElem * def)216 AppendDefElemLeakproof(StringInfo buf, DefElem *def)
217 {
218 if (intVal(def->arg) == 0)
219 {
220 appendStringInfo(buf, " NOT");
221 }
222 appendStringInfo(buf, " LEAKPROOF");
223 }
224
225
226 /*
227 * AppendDefElemSecurity appends a string representing the DefElem to a buffer
228 */
229 static void
AppendDefElemSecurity(StringInfo buf,DefElem * def)230 AppendDefElemSecurity(StringInfo buf, DefElem *def)
231 {
232 if (intVal(def->arg) == 0)
233 {
234 appendStringInfo(buf, " SECURITY INVOKER");
235 }
236 else
237 {
238 appendStringInfo(buf, " SECURITY DEFINER");
239 }
240 }
241
242
243 /*
244 * AppendDefElemParallel appends a string representing the DefElem to a buffer
245 */
246 static void
AppendDefElemParallel(StringInfo buf,DefElem * def)247 AppendDefElemParallel(StringInfo buf, DefElem *def)
248 {
249 appendStringInfo(buf, " PARALLEL %s", CopyAndConvertToUpperCase(strVal(def->arg)));
250 }
251
252
253 /*
254 * AppendDefElemCost appends a string representing the DefElem to a buffer
255 */
256 static void
AppendDefElemCost(StringInfo buf,DefElem * def)257 AppendDefElemCost(StringInfo buf, DefElem *def)
258 {
259 appendStringInfo(buf, " COST %lf", defGetNumeric(def));
260 }
261
262
263 /*
264 * AppendDefElemRows appends a string representing the DefElem to a buffer
265 */
266 static void
AppendDefElemRows(StringInfo buf,DefElem * def)267 AppendDefElemRows(StringInfo buf, DefElem *def)
268 {
269 appendStringInfo(buf, " ROWS %lf", defGetNumeric(def));
270 }
271
272
273 /*
274 * AppendDefElemSet appends a string representing the DefElem to a buffer
275 */
276 static void
AppendDefElemSet(StringInfo buf,DefElem * def)277 AppendDefElemSet(StringInfo buf, DefElem *def)
278 {
279 VariableSetStmt *setStmt = castNode(VariableSetStmt, def->arg);
280
281 AppendVariableSet(buf, setStmt);
282 }
283
284
285 /*
286 * AppendVariableSet appends a string representing the VariableSetStmt to a buffer
287 */
288 void
AppendVariableSet(StringInfo buf,VariableSetStmt * setStmt)289 AppendVariableSet(StringInfo buf, VariableSetStmt *setStmt)
290 {
291 switch (setStmt->kind)
292 {
293 case VAR_SET_VALUE:
294 {
295 AppendVarSetValue(buf, setStmt);
296 break;
297 }
298
299 case VAR_SET_CURRENT:
300 {
301 appendStringInfo(buf, " SET %s FROM CURRENT", quote_identifier(
302 setStmt->name));
303 break;
304 }
305
306 case VAR_SET_DEFAULT:
307 {
308 appendStringInfo(buf, " SET %s TO DEFAULT", quote_identifier(setStmt->name));
309 break;
310 }
311
312 case VAR_RESET:
313 {
314 appendStringInfo(buf, " RESET %s", quote_identifier(setStmt->name));
315 break;
316 }
317
318 case VAR_RESET_ALL:
319 {
320 appendStringInfoString(buf, " RESET ALL");
321 break;
322 }
323
324 /* VAR_SET_MULTI is a special case for SET TRANSACTION that should not occur here */
325 case VAR_SET_MULTI:
326 default:
327 {
328 ereport(ERROR, (errmsg("Unable to deparse SET statement")));
329 break;
330 }
331 }
332 }
333
334
335 /*
336 * AppendVarSetValue deparses a VariableSetStmt with VAR_SET_VALUE kind.
337 * It takes from flatten_set_variable_args in postgres's utils/misc/guc.c,
338 * however flatten_set_variable_args does not apply correct quoting.
339 */
340 static void
AppendVarSetValue(StringInfo buf,VariableSetStmt * setStmt)341 AppendVarSetValue(StringInfo buf, VariableSetStmt *setStmt)
342 {
343 ListCell *varArgCell = NULL;
344 ListCell *firstCell = list_head(setStmt->args);
345
346 Assert(setStmt->kind == VAR_SET_VALUE);
347
348 foreach(varArgCell, setStmt->args)
349 {
350 Node *varArgNode = lfirst(varArgCell);
351 A_Const *varArgConst = NULL;
352 TypeName *typeName = NULL;
353
354 if (IsA(varArgNode, A_Const))
355 {
356 varArgConst = (A_Const *) varArgNode;
357 }
358 else if (IsA(varArgNode, TypeCast))
359 {
360 TypeCast *varArgTypeCast = (TypeCast *) varArgNode;
361
362 varArgConst = castNode(A_Const, varArgTypeCast->arg);
363 typeName = varArgTypeCast->typeName;
364 }
365 else
366 {
367 elog(ERROR, "unrecognized node type: %d", varArgNode->type);
368 }
369
370 /* don't know how to start SET until we inspect first arg */
371 if (varArgCell != firstCell)
372 {
373 appendStringInfoChar(buf, ',');
374 }
375 else if (typeName != NULL)
376 {
377 appendStringInfoString(buf, " SET TIME ZONE");
378 }
379 else
380 {
381 appendStringInfo(buf, " SET %s =", quote_identifier(setStmt->name));
382 }
383
384 Value value = varArgConst->val;
385 switch (value.type)
386 {
387 case T_Integer:
388 {
389 appendStringInfo(buf, " %d", intVal(&value));
390 break;
391 }
392
393 case T_Float:
394 {
395 appendStringInfo(buf, " %s", strVal(&value));
396 break;
397 }
398
399 case T_String:
400 {
401 if (typeName != NULL)
402 {
403 /*
404 * Must be a ConstInterval argument for TIME ZONE. Coerce
405 * to interval and back to normalize the value and account
406 * for any typmod.
407 */
408 Oid typoid = InvalidOid;
409 int32 typmod = -1;
410
411 typenameTypeIdAndMod(NULL, typeName, &typoid, &typmod);
412 Assert(typoid == INTERVALOID);
413
414 Datum interval =
415 DirectFunctionCall3(interval_in,
416 CStringGetDatum(strVal(&value)),
417 ObjectIdGetDatum(InvalidOid),
418 Int32GetDatum(typmod));
419
420 char *intervalout =
421 DatumGetCString(DirectFunctionCall1(interval_out,
422 interval));
423 appendStringInfo(buf, " INTERVAL '%s'", intervalout);
424 }
425 else
426 {
427 appendStringInfo(buf, " %s", quote_literal_cstr(strVal(
428 &value)));
429 }
430 break;
431 }
432
433 default:
434 {
435 elog(ERROR, "Unexpected Value type in VAR_SET_VALUE arguments.");
436 break;
437 }
438 }
439 }
440 }
441
442
443 /*
444 * DeparseRenameFunctionStmt builds and returns a string representing the RenameStmt
445 */
446 char *
DeparseRenameFunctionStmt(Node * node)447 DeparseRenameFunctionStmt(Node *node)
448 {
449 RenameStmt *stmt = castNode(RenameStmt, node);
450 StringInfoData str = { 0 };
451 initStringInfo(&str);
452
453 AssertObjectTypeIsFunctional(stmt->renameType);
454
455 AppendRenameFunctionStmt(&str, stmt);
456
457 return str.data;
458 }
459
460
461 /*
462 * AppendRenameFunctionStmt appends a string representing the RenameStmt to a buffer
463 */
464 static void
AppendRenameFunctionStmt(StringInfo buf,RenameStmt * stmt)465 AppendRenameFunctionStmt(StringInfo buf, RenameStmt *stmt)
466 {
467 ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);
468
469 appendStringInfo(buf, "ALTER %s ", ObjectTypeToKeyword(stmt->renameType));
470 AppendFunctionName(buf, func, stmt->renameType);
471 appendStringInfo(buf, " RENAME TO %s;", quote_identifier(stmt->newname));
472 }
473
474
475 /*
476 * DeparseAlterFunctionSchemaStmt builds and returns a string representing the AlterObjectSchemaStmt
477 */
478 char *
DeparseAlterFunctionSchemaStmt(Node * node)479 DeparseAlterFunctionSchemaStmt(Node *node)
480 {
481 AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
482 StringInfoData str = { 0 };
483 initStringInfo(&str);
484
485 AssertObjectTypeIsFunctional(stmt->objectType);
486
487 AppendAlterFunctionSchemaStmt(&str, stmt);
488
489 return str.data;
490 }
491
492
493 /*
494 * AppendAlterFunctionSchemaStmt appends a string representing the AlterObjectSchemaStmt to a buffer
495 */
496 static void
AppendAlterFunctionSchemaStmt(StringInfo buf,AlterObjectSchemaStmt * stmt)497 AppendAlterFunctionSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt)
498 {
499 ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);
500
501 appendStringInfo(buf, "ALTER %s ", ObjectTypeToKeyword(stmt->objectType));
502 AppendFunctionName(buf, func, stmt->objectType);
503 appendStringInfo(buf, " SET SCHEMA %s;", quote_identifier(stmt->newschema));
504 }
505
506
507 /*
508 * DeparseAlterFunctionOwnerStmt builds and returns a string representing the AlterOwnerStmt
509 */
510 char *
DeparseAlterFunctionOwnerStmt(Node * node)511 DeparseAlterFunctionOwnerStmt(Node *node)
512 {
513 AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
514 StringInfoData str = { 0 };
515 initStringInfo(&str);
516
517 AssertObjectTypeIsFunctional(stmt->objectType);
518
519 AppendAlterFunctionOwnerStmt(&str, stmt);
520
521 return str.data;
522 }
523
524
525 /*
526 * AppendAlterFunctionOwnerStmt appends a string representing the AlterOwnerStmt to a buffer
527 */
528 static void
AppendAlterFunctionOwnerStmt(StringInfo buf,AlterOwnerStmt * stmt)529 AppendAlterFunctionOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)
530 {
531 ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);
532
533 appendStringInfo(buf, "ALTER %s ", ObjectTypeToKeyword(stmt->objectType));
534 AppendFunctionName(buf, func, stmt->objectType);
535 appendStringInfo(buf, " OWNER TO %s;", RoleSpecString(stmt->newowner, true));
536 }
537
538
539 /*
540 * DeparseAlterFunctionDependsStmt builds and returns a string representing the AlterObjectDependsStmt
541 */
542 char *
DeparseAlterFunctionDependsStmt(Node * node)543 DeparseAlterFunctionDependsStmt(Node *node)
544 {
545 AlterObjectDependsStmt *stmt = castNode(AlterObjectDependsStmt, node);
546 StringInfoData str = { 0 };
547 initStringInfo(&str);
548
549 AssertObjectTypeIsFunctional(stmt->objectType);
550
551 AppendAlterFunctionDependsStmt(&str, stmt);
552
553 return str.data;
554 }
555
556
557 /*
558 * AppendAlterFunctionDependsStmt appends a string representing the AlterObjectDependsStmt to a buffer
559 */
560 static void
AppendAlterFunctionDependsStmt(StringInfo buf,AlterObjectDependsStmt * stmt)561 AppendAlterFunctionDependsStmt(StringInfo buf, AlterObjectDependsStmt *stmt)
562 {
563 ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);
564
565 appendStringInfo(buf, "ALTER %s ", ObjectTypeToKeyword(stmt->objectType));
566 AppendFunctionName(buf, func, stmt->objectType);
567 appendStringInfo(buf, " DEPENDS ON EXTENSION %s;", strVal(stmt->extname));
568 }
569
570
571 /*
572 * DeparseDropFunctionStmt builds and returns a string representing the DropStmt
573 */
574 char *
DeparseDropFunctionStmt(Node * node)575 DeparseDropFunctionStmt(Node *node)
576 {
577 DropStmt *stmt = castNode(DropStmt, node);
578 StringInfoData str = { 0 };
579 initStringInfo(&str);
580
581 AssertObjectTypeIsFunctional(stmt->removeType);
582
583 AppendDropFunctionStmt(&str, stmt);
584
585 return str.data;
586 }
587
588
589 /*
590 * AppendDropFunctionStmt appends a string representing the DropStmt to a buffer
591 */
592 static void
AppendDropFunctionStmt(StringInfo buf,DropStmt * stmt)593 AppendDropFunctionStmt(StringInfo buf, DropStmt *stmt)
594 {
595 appendStringInfo(buf, "DROP %s ", ObjectTypeToKeyword(stmt->removeType));
596
597 if (stmt->missing_ok)
598 {
599 appendStringInfoString(buf, "IF EXISTS ");
600 }
601
602 AppendFunctionNameList(buf, stmt->objects, stmt->removeType);
603
604 if (stmt->behavior == DROP_CASCADE)
605 {
606 appendStringInfoString(buf, " CASCADE");
607 }
608
609 appendStringInfoString(buf, ";");
610 }
611
612
613 /*
614 * AppendFunctionNameList appends a string representing the list of function names to a buffer
615 */
616 static void
AppendFunctionNameList(StringInfo buf,List * objects,ObjectType objtype)617 AppendFunctionNameList(StringInfo buf, List *objects, ObjectType objtype)
618 {
619 ListCell *objectCell = NULL;
620 foreach(objectCell, objects)
621 {
622 Node *object = lfirst(objectCell);
623
624 if (objectCell != list_head(objects))
625 {
626 appendStringInfo(buf, ", ");
627 }
628
629 ObjectWithArgs *func = castNode(ObjectWithArgs, object);
630
631 AppendFunctionName(buf, func, objtype);
632 }
633 }
634
635
636 /*
637 * AppendFunctionName appends a string representing a single function name to a buffer
638 */
639 static void
AppendFunctionName(StringInfo buf,ObjectWithArgs * func,ObjectType objtype)640 AppendFunctionName(StringInfo buf, ObjectWithArgs *func, ObjectType objtype)
641 {
642 Oid funcid = LookupFuncWithArgs(objtype, func, true);
643
644 if (funcid == InvalidOid)
645 {
646 /*
647 * DROP FUNCTION IF EXISTS absent_function arrives here
648 *
649 * There is no namespace associated with the nonexistent function,
650 * thus we return the function name as it is provided
651 */
652 char *functionName = NULL;
653 char *schemaName = NULL;
654
655 DeconstructQualifiedName(func->objname, &schemaName, &functionName);
656
657 char *qualifiedFunctionName = quote_qualified_identifier(schemaName,
658 functionName);
659 appendStringInfoString(buf, qualifiedFunctionName);
660
661 if (!func->args_unspecified)
662 {
663 /*
664 * The function is not found, but there is an argument list specified, this has
665 * some known issues with the "any" type. However this is mostly a bug in
666 * postgres' TypeNameListToString. For now the best we can do until we understand
667 * the underlying cause better.
668 */
669
670 const char *args = TypeNameListToString(func->objargs);
671 appendStringInfo(buf, "(%s)", args);
672 }
673 }
674 else
675 {
676 char *functionSignature = format_procedure_qualified(funcid);
677 appendStringInfoString(buf, functionSignature);
678 }
679 }
680
681
682 /*
683 * CopyAndConvertToUpperCase copies a string and converts all characters to uppercase
684 */
685 static char *
CopyAndConvertToUpperCase(const char * str)686 CopyAndConvertToUpperCase(const char *str)
687 {
688 char *result, *p;
689
690 result = pstrdup(str);
691
692 for (p = result; *p; p++)
693 {
694 *p = pg_toupper((unsigned char) *p);
695 }
696
697 return result;
698 }
699