1 /*-------------------------------------------------------------------------
2 *
3 * deparse_type_stmts.c
4 * All routines to deparse type statements.
5 * This file contains all entry points specific for type statement deparsing as well as
6 * functions that are currently only used for deparsing of the type statements.
7 *
8 * Functions that could move later are AppendColumnDef, AppendColumnDefList, etc. These
9 * should be reused across multiple statements and should live in their own deparse
10 * file.
11 *
12 * Copyright (c) Citus Data, Inc.
13 *
14 *-------------------------------------------------------------------------
15 */
16
17 #include "postgres.h"
18
19 #include "catalog/namespace.h"
20 #include "lib/stringinfo.h"
21 #include "nodes/makefuncs.h"
22 #include "nodes/parsenodes.h"
23 #include "parser/parse_type.h"
24 #include "utils/builtins.h"
25
26 #include "distributed/citus_ruleutils.h"
27 #include "distributed/commands.h"
28 #include "distributed/deparser.h"
29 #include "distributed/version_compat.h"
30
31 #define AlterEnumIsRename(stmt) (stmt->oldVal != NULL)
32 #define AlterEnumIsAddValue(stmt) (stmt->oldVal == NULL)
33
34 /* forward declaration for deparse functions */
35 static void AppendCompositeTypeStmt(StringInfo str, CompositeTypeStmt *stmt);
36 static void AppendColumnDef(StringInfo str, ColumnDef *columnDef);
37 static void AppendColumnDefList(StringInfo str, List *columnDefs);
38
39 static void AppendCreateEnumStmt(StringInfo str, CreateEnumStmt *stmt);
40 static void AppendStringList(StringInfo str, List *strings);
41
42 static void AppendDropTypeStmt(StringInfo buf, DropStmt *stmt);
43 static void AppendTypeNameList(StringInfo buf, List *objects);
44
45 static void AppendAlterEnumStmt(StringInfo buf, AlterEnumStmt *stmt);
46
47 static void AppendAlterTypeStmt(StringInfo buf, AlterTableStmt *stmt);
48 static void AppendAlterTypeCmd(StringInfo buf, AlterTableCmd *alterTableCmd);
49 static void AppendAlterTypeCmdAddColumn(StringInfo buf, AlterTableCmd *alterTableCmd);
50 static void AppendAlterTypeCmdDropColumn(StringInfo buf, AlterTableCmd *alterTableCmd);
51 static void AppendAlterTypeCmdAlterColumnType(StringInfo buf,
52 AlterTableCmd *alterTableCmd);
53
54 static void AppendRenameTypeStmt(StringInfo buf, RenameStmt *stmt);
55 static void AppendRenameTypeAttributeStmt(StringInfo buf, RenameStmt *stmt);
56 static void AppendAlterTypeSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);
57 static void AppendAlterTypeOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);
58
59
60 /*
61 * DeparseCompositeTypeStmt builds and returns a string representing the
62 * CompositeTypeStmt for application on a remote server.
63 */
64 char *
DeparseCompositeTypeStmt(Node * node)65 DeparseCompositeTypeStmt(Node *node)
66 {
67 CompositeTypeStmt *stmt = castNode(CompositeTypeStmt, node);
68 StringInfoData sql = { 0 };
69 initStringInfo(&sql);
70
71 AppendCompositeTypeStmt(&sql, stmt);
72
73 return sql.data;
74 }
75
76
77 char *
DeparseCreateEnumStmt(Node * node)78 DeparseCreateEnumStmt(Node *node)
79 {
80 CreateEnumStmt *stmt = castNode(CreateEnumStmt, node);
81 StringInfoData sql = { 0 };
82 initStringInfo(&sql);
83
84 AppendCreateEnumStmt(&sql, stmt);
85
86 return sql.data;
87 }
88
89
90 char *
DeparseAlterEnumStmt(Node * node)91 DeparseAlterEnumStmt(Node *node)
92 {
93 AlterEnumStmt *stmt = castNode(AlterEnumStmt, node);
94 StringInfoData sql = { 0 };
95 initStringInfo(&sql);
96
97 AppendAlterEnumStmt(&sql, stmt);
98
99 return sql.data;
100 }
101
102
103 char *
DeparseDropTypeStmt(Node * node)104 DeparseDropTypeStmt(Node *node)
105 {
106 DropStmt *stmt = castNode(DropStmt, node);
107 StringInfoData str = { 0 };
108 initStringInfo(&str);
109
110 Assert(stmt->removeType == OBJECT_TYPE);
111
112 AppendDropTypeStmt(&str, stmt);
113
114 return str.data;
115 }
116
117
118 char *
DeparseAlterTypeStmt(Node * node)119 DeparseAlterTypeStmt(Node *node)
120 {
121 AlterTableStmt *stmt = castNode(AlterTableStmt, node);
122 StringInfoData str = { 0 };
123 initStringInfo(&str);
124
125 Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_TYPE);
126
127 AppendAlterTypeStmt(&str, stmt);
128
129 return str.data;
130 }
131
132
133 static void
AppendAlterTypeStmt(StringInfo buf,AlterTableStmt * stmt)134 AppendAlterTypeStmt(StringInfo buf, AlterTableStmt *stmt)
135 {
136 const char *identifier = quote_qualified_identifier(stmt->relation->schemaname,
137 stmt->relation->relname);
138 ListCell *cmdCell = NULL;
139
140 Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_TYPE);
141
142 appendStringInfo(buf, "ALTER TYPE %s", identifier);
143 foreach(cmdCell, stmt->cmds)
144 {
145 if (cmdCell != list_head(stmt->cmds))
146 {
147 appendStringInfoString(buf, ", ");
148 }
149
150 AlterTableCmd *alterTableCmd = castNode(AlterTableCmd, lfirst(cmdCell));
151 AppendAlterTypeCmd(buf, alterTableCmd);
152 }
153
154 appendStringInfoString(buf, ";");
155 }
156
157
158 static void
AppendAlterTypeCmd(StringInfo buf,AlterTableCmd * alterTableCmd)159 AppendAlterTypeCmd(StringInfo buf, AlterTableCmd *alterTableCmd)
160 {
161 switch (alterTableCmd->subtype)
162 {
163 case AT_AddColumn:
164 {
165 AppendAlterTypeCmdAddColumn(buf, alterTableCmd);
166 break;
167 }
168
169 case AT_DropColumn:
170 {
171 AppendAlterTypeCmdDropColumn(buf, alterTableCmd);
172 break;
173 }
174
175 case AT_AlterColumnType:
176 {
177 AppendAlterTypeCmdAlterColumnType(buf, alterTableCmd);
178 break;
179 }
180
181 default:
182 {
183 ereport(ERROR, (errmsg("unsupported subtype for alter table command"),
184 errdetail("sub command type: %d", alterTableCmd->subtype)));
185 }
186 }
187 }
188
189
190 static void
AppendAlterTypeCmdAddColumn(StringInfo buf,AlterTableCmd * alterTableCmd)191 AppendAlterTypeCmdAddColumn(StringInfo buf, AlterTableCmd *alterTableCmd)
192 {
193 Assert(alterTableCmd->subtype == AT_AddColumn);
194
195 appendStringInfoString(buf, " ADD ATTRIBUTE ");
196 AppendColumnDef(buf, castNode(ColumnDef, alterTableCmd->def));
197 }
198
199
200 static void
AppendAlterTypeCmdDropColumn(StringInfo buf,AlterTableCmd * alterTableCmd)201 AppendAlterTypeCmdDropColumn(StringInfo buf, AlterTableCmd *alterTableCmd)
202 {
203 Assert(alterTableCmd->subtype == AT_DropColumn);
204 appendStringInfo(buf, " DROP ATTRIBUTE %s", quote_identifier(alterTableCmd->name));
205
206 if (alterTableCmd->behavior == DROP_CASCADE)
207 {
208 appendStringInfoString(buf, " CASCADE");
209 }
210 }
211
212
213 static void
AppendAlterTypeCmdAlterColumnType(StringInfo buf,AlterTableCmd * alterTableCmd)214 AppendAlterTypeCmdAlterColumnType(StringInfo buf, AlterTableCmd *alterTableCmd)
215 {
216 Assert(alterTableCmd->subtype == AT_AlterColumnType);
217 appendStringInfo(buf, " ALTER ATTRIBUTE %s SET DATA TYPE ", quote_identifier(
218 alterTableCmd->name));
219 AppendColumnDef(buf, castNode(ColumnDef, alterTableCmd->def));
220
221 if (alterTableCmd->behavior == DROP_CASCADE)
222 {
223 appendStringInfoString(buf, " CASCADE");
224 }
225 }
226
227
228 static void
AppendAlterEnumStmt(StringInfo buf,AlterEnumStmt * stmt)229 AppendAlterEnumStmt(StringInfo buf, AlterEnumStmt *stmt)
230 {
231 appendStringInfo(buf, "ALTER TYPE %s", NameListToQuotedString(stmt->typeName));
232
233 if (AlterEnumIsRename(stmt))
234 {
235 /* Rename an existing label */
236 appendStringInfo(buf, " RENAME VALUE %s TO %s;",
237 quote_literal_cstr(stmt->oldVal),
238 quote_literal_cstr(stmt->newVal));
239 }
240 else if (AlterEnumIsAddValue(stmt))
241 {
242 /* Add a new label */
243 appendStringInfoString(buf, " ADD VALUE ");
244 if (stmt->skipIfNewValExists)
245 {
246 appendStringInfoString(buf, "IF NOT EXISTS ");
247 }
248 appendStringInfoString(buf, quote_literal_cstr(stmt->newVal));
249
250 if (stmt->newValNeighbor)
251 {
252 appendStringInfo(buf, " %s %s",
253 stmt->newValIsAfter ? "AFTER" : "BEFORE",
254 quote_literal_cstr(stmt->newValNeighbor));
255 }
256
257 appendStringInfoString(buf, ";");
258 }
259 }
260
261
262 static void
AppendDropTypeStmt(StringInfo buf,DropStmt * stmt)263 AppendDropTypeStmt(StringInfo buf, DropStmt *stmt)
264 {
265 /*
266 * already tested at call site, but for future it might be collapsed in a
267 * DeparseDropStmt so be safe and check again
268 */
269 Assert(stmt->removeType == OBJECT_TYPE);
270
271 appendStringInfo(buf, "DROP TYPE ");
272 if (stmt->missing_ok)
273 {
274 appendStringInfoString(buf, "IF EXISTS ");
275 }
276 AppendTypeNameList(buf, stmt->objects);
277 if (stmt->behavior == DROP_CASCADE)
278 {
279 appendStringInfoString(buf, " CASCADE");
280 }
281 appendStringInfoString(buf, ";");
282 }
283
284
285 static void
AppendTypeNameList(StringInfo buf,List * objects)286 AppendTypeNameList(StringInfo buf, List *objects)
287 {
288 ListCell *objectCell = NULL;
289 foreach(objectCell, objects)
290 {
291 TypeName *typeName = castNode(TypeName, lfirst(objectCell));
292 Oid typeOid = LookupTypeNameOid(NULL, typeName, false);
293 const char *identifier = format_type_be_qualified(typeOid);
294
295 if (objectCell != list_head(objects))
296 {
297 appendStringInfo(buf, ", ");
298 }
299
300 appendStringInfoString(buf, identifier);
301 }
302 }
303
304
305 /*
306 * AppendCompositeTypeStmt appends the sql string to recreate a CompositeTypeStmt to the
307 * provided buffer, ending in a ; for concatination of multiple statements.
308 */
309 static void
AppendCompositeTypeStmt(StringInfo str,CompositeTypeStmt * stmt)310 AppendCompositeTypeStmt(StringInfo str, CompositeTypeStmt *stmt)
311 {
312 const char *identifier = quote_qualified_identifier(stmt->typevar->schemaname,
313 stmt->typevar->relname);
314 appendStringInfo(str, "CREATE TYPE %s AS (", identifier);
315 AppendColumnDefList(str, stmt->coldeflist);
316 appendStringInfo(str, ");");
317 }
318
319
320 static void
AppendCreateEnumStmt(StringInfo str,CreateEnumStmt * stmt)321 AppendCreateEnumStmt(StringInfo str, CreateEnumStmt *stmt)
322 {
323 RangeVar *typevar = makeRangeVarFromNameList(stmt->typeName);
324
325 /* create the identifier from the fully qualified rangevar */
326 const char *identifier = quote_qualified_identifier(typevar->schemaname,
327 typevar->relname);
328
329 appendStringInfo(str, "CREATE TYPE %s AS ENUM (", identifier);
330 AppendStringList(str, stmt->vals);
331 appendStringInfo(str, ");");
332 }
333
334
335 static void
AppendStringList(StringInfo str,List * strings)336 AppendStringList(StringInfo str, List *strings)
337 {
338 ListCell *stringCell = NULL;
339 foreach(stringCell, strings)
340 {
341 const char *string = strVal(lfirst(stringCell));
342 if (stringCell != list_head(strings))
343 {
344 appendStringInfoString(str, ", ");
345 }
346
347 string = quote_literal_cstr(string);
348 appendStringInfoString(str, string);
349 }
350 }
351
352
353 /*
354 * AppendColumnDefList appends the definition of a list of ColumnDef items to the provided
355 * buffer, adding separators as necessary.
356 */
357 static void
AppendColumnDefList(StringInfo str,List * columnDefs)358 AppendColumnDefList(StringInfo str, List *columnDefs)
359 {
360 ListCell *columnDefCell = NULL;
361 foreach(columnDefCell, columnDefs)
362 {
363 if (columnDefCell != list_head(columnDefs))
364 {
365 appendStringInfoString(str, ", ");
366 }
367 AppendColumnDef(str, castNode(ColumnDef, lfirst(columnDefCell)));
368 }
369 }
370
371
372 /*
373 * AppendColumnDef appends the definition of one ColumnDef completely qualified to the
374 * provided buffer.
375 *
376 * If the colname is not set that part is ommitted. This is the case in alter column type
377 * statements.
378 */
379 static void
AppendColumnDef(StringInfo str,ColumnDef * columnDef)380 AppendColumnDef(StringInfo str, ColumnDef *columnDef)
381 {
382 int32 typmod = 0;
383 Oid typeOid = InvalidOid;
384 bits16 formatFlags = FORMAT_TYPE_TYPEMOD_GIVEN | FORMAT_TYPE_FORCE_QUALIFY;
385
386 typenameTypeIdAndMod(NULL, columnDef->typeName, &typeOid, &typmod);
387 Oid collationOid = GetColumnDefCollation(NULL, columnDef, typeOid);
388
389 Assert(!columnDef->is_not_null); /* not null is not supported on composite types */
390
391 if (columnDef->colname)
392 {
393 appendStringInfo(str, "%s ", quote_identifier(columnDef->colname));
394 }
395
396 appendStringInfo(str, "%s", format_type_extended(typeOid, typmod, formatFlags));
397
398 if (OidIsValid(collationOid))
399 {
400 const char *identifier = FormatCollateBEQualified(collationOid);
401 appendStringInfo(str, " COLLATE %s", identifier);
402 }
403 }
404
405
406 char *
DeparseRenameTypeStmt(Node * node)407 DeparseRenameTypeStmt(Node *node)
408 {
409 RenameStmt *stmt = castNode(RenameStmt, node);
410 StringInfoData str = { 0 };
411 initStringInfo(&str);
412
413 Assert(stmt->renameType == OBJECT_TYPE);
414
415 AppendRenameTypeStmt(&str, stmt);
416
417 return str.data;
418 }
419
420
421 static void
AppendRenameTypeStmt(StringInfo buf,RenameStmt * stmt)422 AppendRenameTypeStmt(StringInfo buf, RenameStmt *stmt)
423 {
424 List *names = (List *) stmt->object;
425
426 appendStringInfo(buf, "ALTER TYPE %s RENAME TO %s;", NameListToQuotedString(names),
427 quote_identifier(stmt->newname));
428 }
429
430
431 char *
DeparseRenameTypeAttributeStmt(Node * node)432 DeparseRenameTypeAttributeStmt(Node *node)
433 {
434 RenameStmt *stmt = castNode(RenameStmt, node);
435 StringInfoData str = { 0 };
436 initStringInfo(&str);
437
438 Assert(stmt->renameType == OBJECT_ATTRIBUTE);
439 Assert(stmt->relationType == OBJECT_TYPE);
440
441 AppendRenameTypeAttributeStmt(&str, stmt);
442
443 return str.data;
444 }
445
446
447 static void
AppendRenameTypeAttributeStmt(StringInfo buf,RenameStmt * stmt)448 AppendRenameTypeAttributeStmt(StringInfo buf, RenameStmt *stmt)
449 {
450 appendStringInfo(buf, "ALTER TYPE %s RENAME ATTRIBUTE %s TO %s",
451 quote_qualified_identifier(stmt->relation->schemaname,
452 stmt->relation->relname),
453 quote_identifier(stmt->subname),
454 quote_identifier(stmt->newname));
455
456 if (stmt->behavior == DROP_CASCADE)
457 {
458 appendStringInfoString(buf, " CASCADE");
459 }
460
461 appendStringInfoString(buf, ";");
462 }
463
464
465 char *
DeparseAlterTypeSchemaStmt(Node * node)466 DeparseAlterTypeSchemaStmt(Node *node)
467 {
468 AlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);
469 StringInfoData str = { 0 };
470 initStringInfo(&str);
471
472 Assert(stmt->objectType == OBJECT_TYPE);
473
474 AppendAlterTypeSchemaStmt(&str, stmt);
475
476 return str.data;
477 }
478
479
480 static void
AppendAlterTypeSchemaStmt(StringInfo buf,AlterObjectSchemaStmt * stmt)481 AppendAlterTypeSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt)
482 {
483 Assert(stmt->objectType == OBJECT_TYPE);
484
485 List *names = (List *) stmt->object;
486 appendStringInfo(buf, "ALTER TYPE %s SET SCHEMA %s;", NameListToQuotedString(names),
487 quote_identifier(stmt->newschema));
488 }
489
490
491 char *
DeparseAlterTypeOwnerStmt(Node * node)492 DeparseAlterTypeOwnerStmt(Node *node)
493 {
494 AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
495 StringInfoData str = { 0 };
496 initStringInfo(&str);
497
498 Assert(stmt->objectType == OBJECT_TYPE);
499
500 AppendAlterTypeOwnerStmt(&str, stmt);
501
502 return str.data;
503 }
504
505
506 static void
AppendAlterTypeOwnerStmt(StringInfo buf,AlterOwnerStmt * stmt)507 AppendAlterTypeOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)
508 {
509 Assert(stmt->objectType == OBJECT_TYPE);
510
511 List *names = (List *) stmt->object;
512 appendStringInfo(buf, "ALTER TYPE %s OWNER TO %s;", NameListToQuotedString(names),
513 RoleSpecString(stmt->newowner, true));
514 }
515