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