1# -*- coding: utf-8 -*-
2# :Project:   pglast -- Printer function for SQL DDL nodes
3# :Created:   gio 09 nov 2017 10:50:30 CET
4# :Author:    Lele Gaifax <lele@metapensiero.it>
5# :License:   GNU General Public License version 3 or later
6# :Copyright: © 2017, 2018 Lele Gaifax
7#
8
9from .. import enums
10from ..node import Missing, List
11from ..printer import node_printer
12
13
14@node_printer('ColumnDef')
15def column_def(node, output):
16    output.print_name(node.colname)
17    output.space()
18    if node.typeName:
19        output.print_name(node.typeName)
20    else:
21        if node.constraints:
22            output.write('WITH OPTIONS ')
23    if node.collClause:
24        output.print_node(node.collClause)
25    if node.is_not_null:
26        # FIXME: find a way to get here
27        output.swrite('NOT NULL')
28    if node.constraints:
29        output.print_list(node.constraints, '', standalone_items=False)
30
31
32OBJECT_NAMES = {
33    enums.ObjectType.OBJECT_ACCESS_METHOD: 'ACCESS METHOD',
34    enums.ObjectType.OBJECT_AGGREGATE: 'AGGREGATE',
35    enums.ObjectType.OBJECT_AMOP: 'AMOP',
36    enums.ObjectType.OBJECT_AMPROC: 'AMPROC',
37    enums.ObjectType.OBJECT_ATTRIBUTE: 'ATTRIBUTE',
38    enums.ObjectType.OBJECT_CAST: 'CAST',
39    enums.ObjectType.OBJECT_COLUMN: 'COLUMN',
40    enums.ObjectType.OBJECT_COLLATION: 'COLLATION',
41    enums.ObjectType.OBJECT_CONVERSION: 'CONVERSION',
42    enums.ObjectType.OBJECT_DATABASE: 'DATABASE',
43    enums.ObjectType.OBJECT_DEFAULT: 'DEFAULT',
44    enums.ObjectType.OBJECT_DEFACL: 'DEFACL',
45    enums.ObjectType.OBJECT_DOMAIN: 'DOMAIN',
46    enums.ObjectType.OBJECT_DOMCONSTRAINT: 'CONSTRAINT',
47    enums.ObjectType.OBJECT_EVENT_TRIGGER: 'EVENT TRIGGER',
48    enums.ObjectType.OBJECT_EXTENSION: 'EXTENSION',
49    enums.ObjectType.OBJECT_FDW: 'FOREIGN DATA WRAPPER',
50    enums.ObjectType.OBJECT_FOREIGN_SERVER: 'SERVER',
51    enums.ObjectType.OBJECT_FOREIGN_TABLE: 'FOREIGN TABLE',
52    enums.ObjectType.OBJECT_FUNCTION: 'FUNCTION',
53    enums.ObjectType.OBJECT_INDEX: 'INDEX',
54    enums.ObjectType.OBJECT_LANGUAGE: 'LANGUAGE',
55    enums.ObjectType.OBJECT_LARGEOBJECT: 'LARGE OBJECT',
56    enums.ObjectType.OBJECT_MATVIEW: 'MATERIALIZED VIEW',
57    enums.ObjectType.OBJECT_OPCLASS: 'OPERATOR CLASS',
58    enums.ObjectType.OBJECT_OPERATOR: 'OPERATOR',
59    enums.ObjectType.OBJECT_OPFAMILY: 'OPERATOR FAMILY',
60    enums.ObjectType.OBJECT_POLICY: 'POLICY',
61    enums.ObjectType.OBJECT_PUBLICATION: 'PUBLICATION',
62    enums.ObjectType.OBJECT_PUBLICATION_REL: 'PUBLICATION_REL',
63    enums.ObjectType.OBJECT_ROLE: 'ROLE',
64    enums.ObjectType.OBJECT_RULE: 'RULE',
65    enums.ObjectType.OBJECT_SCHEMA: 'SCHEMA',
66    enums.ObjectType.OBJECT_SEQUENCE: 'SEQUENCE',
67    enums.ObjectType.OBJECT_SUBSCRIPTION: 'SUBSCRIPTION',
68    enums.ObjectType.OBJECT_STATISTIC_EXT: 'STATISTICS',
69    enums.ObjectType.OBJECT_TABCONSTRAINT: 'CONSTRAINT',
70    enums.ObjectType.OBJECT_TABLE: 'TABLE',
71    enums.ObjectType.OBJECT_TABLESPACE: 'TABLESPACE',
72    enums.ObjectType.OBJECT_TRANSFORM: 'TRANSFORM',
73    enums.ObjectType.OBJECT_TRIGGER: 'TRIGGER',
74    enums.ObjectType.OBJECT_TSCONFIGURATION: 'TEXT SEARCH CONFIGURATION',
75    enums.ObjectType.OBJECT_TSDICTIONARY: 'TEXT SEARCH DICTIONARY',
76    enums.ObjectType.OBJECT_TSPARSER: 'TEXT SEARCH PARSER',
77    enums.ObjectType.OBJECT_TSTEMPLATE: 'TEXT SEARCH TEMPLATE',
78    enums.ObjectType.OBJECT_TYPE: 'TYPE',
79    enums.ObjectType.OBJECT_USER_MAPPING: 'USER_MAPPING',
80    enums.ObjectType.OBJECT_VIEW: 'VIEW',
81}
82
83
84@node_printer('CommentStmt')
85def comment_stmt(node, output):
86    otypes = enums.ObjectType
87    output.write('COMMENT ')
88    output.write('ON ')
89    output.writes(OBJECT_NAMES[node.objtype.value])
90    if node.objtype in (otypes.OBJECT_OPCLASS, otypes.OBJECT_OPFAMILY):
91        nodes = list(node.object)
92        using = nodes.pop(0)
93        output.print_name(nodes)
94        output.write(' USING ')
95        output.print_name(using)
96    elif node.objtype in (otypes.OBJECT_TABCONSTRAINT, otypes.OBJECT_POLICY,
97                          otypes.OBJECT_RULE, otypes.OBJECT_TRIGGER):
98        nodes = list(node.object)
99        output.print_name(nodes.pop())
100        output.write(' ON ')
101        output.print_name(nodes)
102    elif node.objtype == otypes.OBJECT_DOMCONSTRAINT:
103        nodes = list(node.object)
104        output.print_name(nodes.pop())
105        output.write(' ON DOMAIN ')
106        output.print_name(nodes)
107    elif node.objtype == otypes.OBJECT_TRANSFORM:
108        nodes = list(node.object)
109        output.write('FOR ')
110        output.print_name(nodes.pop(0))
111        output.write(' LANGUAGE ')
112        output.print_name(nodes)
113    elif isinstance(node.object, List):
114        if node.object[0].node_tag != 'String':
115            output.write(' (')
116            output.print_list(node.object, ' AS ', standalone_items=False)
117            output.write(')')
118        else:
119            output.print_name(node.object)
120    else:
121        output.print_name(node.object)
122    output.newline()
123    output.space(2)
124    output.write('IS ')
125    with output.push_indent():
126        output._write_quoted_string(node.comment.value)
127
128
129@node_printer('Constraint')
130def constraint(node, output):
131    if node.conname:
132        output.swrite('CONSTRAINT ')
133        output.print_name(node.conname)
134    ct = enums.ConstrType
135    if node.contype == ct.CONSTR_NULL:
136        output.swrite('NULL')
137    elif node.contype == ct.CONSTR_DEFAULT:
138        output.swrite('DEFAULT ')
139        # """
140        # we may have the expression in either "raw" form [...]  or "cooked" form [...]
141        # should never have both in the same node!
142        # """
143        output.print_node(node.raw_expr or node.cooked_expr)
144    elif node.contype == ct.CONSTR_NOTNULL:
145        output.swrite('NOT NULL')
146    elif node.contype == ct.CONSTR_CHECK:
147        output.swrite('CHECK (')
148        output.print_node(node.raw_expr or node.cooked_expr)
149        output.write(')')
150        if node.is_no_inherit:
151            output.swrite('NO INHERIT')
152    elif node.contype == ct.CONSTR_PRIMARY:
153        output.swrite('PRIMARY KEY')
154    elif node.contype == ct.CONSTR_UNIQUE:
155        output.swrite('UNIQUE')
156    elif node.contype == ct.CONSTR_EXCLUSION:
157        output.swrite('EXCLUDE USING ')
158        if node.access_method:
159            output.print_node(node.access_method)
160            output.write(' ')
161        output.write('(')
162        first = True
163        for elem, clauses in node.exclusions:
164            if first:
165                first = False
166            else:
167                output.write(', ')
168            output.print_node(elem)
169            output.swrite('WITH ')
170            output.write(clauses.string_value)
171        output.write(')')
172    elif node.contype == ct.CONSTR_FOREIGN:
173        if node.fk_attrs:
174            output.swrite('FOREIGN KEY ')
175            output.write(' (')
176            output.print_name(node.fk_attrs, ',')
177            output.write(')')
178        output.swrite('REFERENCES ')
179        output.print_name(node.pktable)
180        output.write(' (')
181        output.print_name(node.pk_attrs, ',')
182        output.write(')')
183        if node.fk_matchtype != enums.FKCONSTR_MATCH_SIMPLE:
184            output.write(' MATCH ')
185            if node.fk_matchtype == enums.FKCONSTR_MATCH_FULL:
186                output.write('FULL')
187            elif node.fk_matchtype == enums.FKCONSTR_MATCH_PARTIAL:  # pragma: no cover
188                # MATCH PARTIAL not yet implemented
189                output.write('PARTIAL')
190        if node.fk_del_action != enums.FKCONSTR_ACTION_NOACTION:
191            output.write(' ON DELETE ')
192            if node.fk_del_action == enums.FKCONSTR_ACTION_RESTRICT:
193                output.write('RESTRICT')
194            elif node.fk_del_action == enums.FKCONSTR_ACTION_CASCADE:
195                output.write('CASCADE')
196            elif node.fk_del_action == enums.FKCONSTR_ACTION_SETNULL:
197                output.write('SET NULL')
198            elif node.fk_del_action == enums.FKCONSTR_ACTION_SETDEFAULT:
199                output.write('SET DEFAULT')
200        if node.fk_upd_action != enums.FKCONSTR_ACTION_NOACTION:
201            output.write(' ON UPDATE ')
202            if node.fk_upd_action == enums.FKCONSTR_ACTION_RESTRICT:
203                output.write('RESTRICT')
204            elif node.fk_upd_action == enums.FKCONSTR_ACTION_CASCADE:
205                output.write('CASCADE')
206            elif node.fk_upd_action == enums.FKCONSTR_ACTION_SETNULL:
207                output.write('SET NULL')
208            elif node.fk_upd_action == enums.FKCONSTR_ACTION_SETDEFAULT:
209                output.write('SET DEFAULT')
210
211    # Common to UNIQUE & PRIMARY_KEY
212    if node.keys:
213        output.write(' (')
214        output.print_name(node.keys, ',')
215        output.write(')')
216    with output.push_indent():
217        first = True
218        if node.options:
219            output.write(' WITH (')
220            output.print_list(node.options)
221            output.write(')')
222            first = False
223        if node.indexspace:
224            if first:
225                first = False
226            else:
227                output.newline()
228            output.write(' USING INDEX TABLESPACE ')
229            output.print_name(node.indexspace)
230
231
232@node_printer('CreateAmStmt')
233def create_am_stmt(node, output):
234    output.write('CREATE ACCESS METHOD ')
235    output.print_name(node.amname)
236    if node.amtype == 'i':
237        output.write(' TYPE INDEX HANDLER ')
238        output.print_name(node.handler_name)
239    else:  # pragma: nocover
240        raise NotImplementedError
241
242
243@node_printer('CreateCastStmt')
244def create_cast_stmt(node, output):
245    output.write('CREATE CAST (')
246    output.print_node(node.sourcetype)
247    output.write(' AS ')
248    output.print_node(node.targettype)
249    output.write(') ')
250    if node.func:
251        output.write('WITH FUNCTION ')
252        output.print_node(node.func)
253    elif node.inout:
254        output.write('WITH INOUT')
255    else:
256        output.write('WITHOUT FUNCTION')
257    if node.context == enums.CoercionContext.COERCION_ASSIGNMENT:
258        output.write(' AS ASSIGNMENT')
259    elif node.context == enums.CoercionContext.COERCION_IMPLICIT:
260        output.write(' AS IMPLICIT')
261
262
263@node_printer('CreateConversionStmt')
264def create_conversion_stmt(node, output):
265    output.write('CREATE ')
266    if node['def']:
267        output.write('DEFAULT ')
268    output.write('CONVERSION ')
269    output.print_name(node.conversion_name)
270    output.write(" FOR '%s' TO '%s'" % (node.for_encoding_name.value,
271                                        node.to_encoding_name.value))
272    output.write(' FROM ')
273    output.print_name(node.func_name)
274
275
276@node_printer('CreateDomainStmt')
277def create_domain_stmt(node, output):
278    output.write('CREATE DOMAIN ')
279    output.print_name(node.domainname)
280    output.write(' AS ')
281    output.print_node(node.typeName)
282    if node.collClause:
283        output.print_node(node.collClause)
284    if node.constraints:
285        output.print_list(node.constraints, '', standalone_items=False)
286
287
288@node_printer('CreateEventTrigStmt')
289def create_event_trig_stmt(node, output):
290    output.write('CREATE EVENT TRIGGER ')
291    output.print_name(node.trigname)
292    output.write(' ON ')
293    output.print_name(node.eventname)
294    output.newline()
295    with output.push_indent(2):
296        if node.whenclause:
297            output.write('WHEN ')
298            output.print_list(node.whenclause, 'AND', relative_indent=-4)
299            output.newline()
300        output.write('EXECUTE PROCEDURE ')
301        output.print_name(node.funcname)
302        output.write('()')
303
304
305@node_printer('CreateEventTrigStmt', 'DefElem')
306def create_event_trig_stmt_def_elem(node, output):
307    output.print_name(node.defname)
308    output.write(' IN (')
309    output.print_list(node.arg, standalone_items=False)
310    output.write(')')
311
312
313@node_printer('CreateExtensionStmt')
314def create_extension_stmt(node, output):
315    output.write('CREATE EXTENSION ')
316    if node.if_not_exists:
317        output.write('IF NOT EXISTS ')
318    output.print_name(node.extname)
319    if node.options:
320        output.newline()
321        output.space(2)
322        output.write('WITH ')
323        output.print_list(node.options, '')
324
325
326@node_printer('CreateExtensionStmt', 'DefElem')
327def create_extension_stmt_def_elem(node, output):
328    option = node.defname.value
329    if option == 'cascade':
330        if node.arg.ival == 1:
331            output.write('CASCADE')
332    elif option == 'old_version':
333        output.write('from ')
334        output.print_node(node.arg)
335    elif option == 'new_version':
336        output.write('version ')
337        output.print_node(node.arg)
338    else:
339        output.write(option)
340        output.write(' ')
341        if option == 'schema':
342            output.print_name(node.arg)
343        else:
344            output.print_node(node.arg)
345
346
347@node_printer('CreateFdwStmt')
348def create_fdw_stmt(node, output):
349    output.write('CREATE FOREIGN DATA WRAPPER ')
350    output.print_name(node.fdwname)
351    if node.func_options:
352        output.newline()
353        output.space(2)
354        with output.push_indent():
355            output.print_list(node.func_options, '')
356    if node.options:
357        output.newline()
358        with output.push_indent(2):
359            output.write('OPTIONS (')
360            output.print_list(node.options)
361            output.write(')')
362
363
364@node_printer('CreateFdwStmt', 'DefElem')
365def create_fdw_stmt_def_elem(node, output):
366    if node.parent_attribute[0] == 'options':
367        output.write(node.defname.value)
368        output.write(' ')
369        output.print_node(node.arg)
370    else:
371        output.write(node.defname.value.upper())
372        output.write(' ')
373        output.print_name(node.arg)
374
375
376@node_printer('CreateForeignTableStmt')
377def create_foreign_table_stmt(node, output):
378    output.print_node(node.base)
379    if not node.base.tableElts:
380        output.newline()
381        output.write(' ')
382    output.write(' SERVER ')
383    output.print_name(node.servername)
384    if node.options:
385        output.newline()
386        with output.push_indent(2):
387            output.write('OPTIONS (')
388            output.print_list(node.options)
389            output.write(')')
390
391
392@node_printer('CreateForeignTableStmt', 'DefElem')
393def create_foreign_table_stmt_def_elem(node, output):
394    output.write(node.defname.value)
395    output.write(' ')
396    output.print_node(node.arg)
397
398
399@node_printer('CreateSchemaStmt')
400def create_schema_stmt(node, output):
401    output.write('CREATE SCHEMA ')
402    if node.if_not_exists:
403        output.write('IF NOT EXISTS ')
404    if node.schemaname:
405        output.print_name(node.schemaname)
406    if node.authrole:
407        output.swrite('AUTHORIZATION ')
408        output.print_node(node.authrole)
409    if node.schemaElts:
410        output.newline()
411        output.space(2)
412        with output.push_indent():
413            output.print_list(node.schemaElts, '', standalone_items=True)
414
415
416@node_printer('CreateSeqStmt')
417def create_seq_stmt(node, output):
418    output.write('CREATE ')
419    if node.sequence.relpersistence == enums.RELPERSISTENCE_TEMP:
420        output.writes('TEMPORARY')
421    output.writes('SEQUENCE')
422    if node.if_not_exists:
423        output.writes('IF NOT EXISTS')
424    if node.sequence.schemaname is not Missing:
425        output.print_name(node.sequence.schemaname)
426        output.write('.')
427    output.print_name(node.sequence.relname)
428    if node.options:
429        output.newline()
430        output.space(2)
431        with output.push_indent():
432            output.print_list(node.options, '')
433
434
435@node_printer('CreateSeqStmt', 'DefElem')
436def create_seq_stmt_def_elem(node, output):
437    option = node.defname.value
438    if option == 'cycle':
439        if node.arg.ival.value == 0:
440            output.write('NO ')
441        output.write('CYCLE')
442    elif option == 'increment':
443        output.write('INCREMENT BY ')
444        output.print_node(node.arg)
445    elif option == 'owned_by':
446        output.write('OWNED BY ')
447        output.print_name(node.arg)
448    elif option == 'start':
449        output.write('START WITH ')
450        output.print_node(node.arg)
451    else:
452        if node.arg is Missing:
453            output.write('NO ')
454        output.write(option.upper())
455        if node.arg is not Missing:
456            output.write(' ')
457            output.print_node(node.arg)
458    if node.defaction != enums.DefElemAction.DEFELEM_UNSPEC:  # pragma: nocover
459        raise NotImplementedError
460
461
462@node_printer('CreateStmt')
463def create_stmt(node, output):
464    output.writes('CREATE')
465    if node.parent_node.node_tag == 'CreateForeignTableStmt':
466        output.writes('FOREIGN')
467    else:
468        if node.relation.relpersistence == enums.RELPERSISTENCE_TEMP:
469            output.writes('TEMPORARY')
470        elif node.relation.relpersistence == enums.RELPERSISTENCE_UNLOGGED:
471            output.writes('UNLOGGED')
472    output.writes('TABLE')
473    if node.if_not_exists:
474        output.writes('IF NOT EXISTS')
475    output.print_node(node.relation)
476    if node.ofTypename:
477        output.write(' OF ')
478        output.print_name(node.ofTypename)
479    if node.partbound:
480        output.write(' PARTITION OF ')
481        output.print_list(node.inhRelations)
482    if node.tableElts:
483        output.write(' (')
484        output.newline()
485        output.space(2 if output.comma_at_eoln or len(node.tableElts) == 1 else 4)
486        output.print_list(node.tableElts)
487        output.newline()
488        output.write(')')
489    elif node.partbound:
490        output.newline()
491        output.write(' ')
492    with output.push_indent():
493        first = True
494        if node.inhRelations and not node.partbound:
495            output.write(' INHERITS (')
496            output.print_list(node.inhRelations)
497            output.write(')')
498            first = False
499        if node.partbound:
500            if first:
501                first = False
502            else:  # pragma: nocover
503                output.newline()
504            output.write(' FOR VALUES ')
505            output.print_node(node.partbound)
506        if node.partspec:
507            if first:
508                first = False
509            else:  # pragma: nocover
510                output.newline()
511            output.write(' PARTITION BY ')
512            output.print_node(node.partspec)
513        if node.options:
514            if first:
515                first = False
516            else:
517                output.newline()
518            if len(node.options) == 1 and node.options[0].defname == 'oids':
519                output.write(' WITH')
520                if node.options[0].arg.ival.value == 0:
521                    output.write
522                    output.write('OUT')
523                output.write(' OIDS')
524            else:
525                output.write(' WITH (')
526                output.print_list(node.options)
527                output.write(')')
528        if node.oncommit != enums.OnCommitAction.ONCOMMIT_NOOP:
529            if first:
530                first = False
531            else:
532                output.newline()
533            output.write(' ON COMMIT ')
534            if node.oncommit == enums.OnCommitAction.ONCOMMIT_PRESERVE_ROWS:
535                output.write('PRESERVE ROWS')
536            elif node.oncommit == enums.OnCommitAction.ONCOMMIT_DELETE_ROWS:
537                output.write('DELETE ROWS')
538            elif node.oncommit == enums.OnCommitAction.ONCOMMIT_DROP:
539                output.write('DROP')
540        if node.tablespacename:
541            if first:
542                first = False
543            else:
544                output.newline()
545            output.write(' TABLESPACE ')
546            output.print_name(node.tablespacename)
547
548
549@node_printer('CreateTableAsStmt')
550def create_table_as_stmt(node, output):
551    output.writes('CREATE')
552    into = node.into
553    rel = into.rel
554    if rel.relpersistence == enums.RELPERSISTENCE_TEMP:
555        output.writes('TEMPORARY')
556    elif rel.relpersistence == enums.RELPERSISTENCE_UNLOGGED:
557        output.writes('UNLOGGED')
558    output.writes('TABLE')
559    if node.if_not_exists:
560        output.writes('IF NOT EXISTS')
561    output.print_node(rel)
562    if into.colNames:
563        output.write(' (')
564        output.print_name(into.colNames, ',')
565        output.write(')')
566    output.newline()
567    if into.options:
568        if len(into.options) == 1 and into.options[0].defname == 'oids':
569            output.space(2)
570            output.write('WITH')
571            if into.options[0].arg.ival.value == 0:
572                output.write
573                output.write('OUT')
574            output.write(' OIDS')
575        else:
576            output.space(2)
577            output.write('WITH (')
578            output.print_list(into.options)
579            output.write(')')
580        output.newline()
581    if into.onCommit != enums.OnCommitAction.ONCOMMIT_NOOP:
582        output.space(2)
583        output.write('ON COMMIT ')
584        if into.onCommit == enums.OnCommitAction.ONCOMMIT_PRESERVE_ROWS:
585            output.write('PRESERVE ROWS')
586        elif into.onCommit == enums.OnCommitAction.ONCOMMIT_DELETE_ROWS:
587            output.write('DELETE ROWS')
588        elif into.onCommit == enums.OnCommitAction.ONCOMMIT_DROP:
589            output.write('DROP')
590        output.newline()
591    if into.tableSpaceName:
592        output.space(2)
593        output.write('TABLESPACE ')
594        output.print_name(into.tableSpaceName)
595        output.newline()
596    output.space(2)
597    output.write('AS ')
598    if ((node.query.targetList is not Missing
599         and node.query.whereClause is Missing
600         and len(node.query.targetList[0].val.fields) == 1
601         and node.query.targetList[0].val.fields[0].node_tag == 'A_Star')):
602        output.write('TABLE ')
603        output.print_list(node.query.fromClause)
604    else:
605        with output.push_indent():
606            output.print_node(node.query)
607    if node.into.skipData:
608        output.newline()
609        output.space(2)
610        output.write('WITH NO DATA')
611
612
613@node_printer('CreatedbStmt')
614def createdb_stmt(node, output):
615    output.write('CREATE DATABASE ')
616    output.print_name(node.dbname)
617    if node.options:
618        output.newline()
619        output.space(2)
620        output.write('WITH ')
621        output.print_list(node.options, '')
622
623
624@node_printer('CreatedbStmt', 'DefElem')
625def create_db_stmt_def_elem(node, output):
626    option = node.defname.value
627    if option == 'connection_limit':
628        output.write('connection limit')
629    else:
630        output.print_node(node.defname)
631    if node.arg is not Missing:
632        output.write(' = ')
633        if isinstance(node.arg, List) or option in ('allow_connections', 'is_template'):
634            output.write(node.arg.string_value)
635        else:
636            output.print_node(node.arg)
637
638
639@node_printer('DefineStmt')
640def define_stmt(node, output):
641    output.write('CREATE ')
642    output.writes(OBJECT_NAMES[node.kind.value])
643    if node.if_not_exists:
644        output.write('IF NOT EXISTS ')
645    output.print_list(node.defnames, '.', standalone_items=False, are_names=True,
646                      is_symbol=node.kind == enums.ObjectType.OBJECT_OPERATOR)
647    output.write(' ')
648    if node.args is not Missing:
649        output.write('(')
650        # args is actually a tuple (list-of-nodes, integer): the integer value, if different
651        # from -1, is the number of nodes representing the actual arguments, remaining are
652        # ORDER BY
653        args, count = node.args
654        count = count.ival.value
655        if count == -1:
656            output.print_list(args, standalone_items=False)
657        else:
658            output.print_list(args[:count], standalone_items=False)
659            output.write(' ORDER BY ')
660            output.print_list(args[count:], standalone_items=False)
661        output.write(') ')
662    if ((node.kind == enums.ObjectType.OBJECT_COLLATION
663         and len(node.definition) == 1
664         and node.definition[0].defname == 'from')):
665        output.write('FROM ')
666        output.print_name(node.definition[0].arg)
667    else:
668        output.write('(')
669        output.newline()
670        output.space(2 if output.comma_at_eoln or len(node.definition) == 1 else 4)
671        output.print_list(node.definition)
672        output.newline()
673        output.write(')')
674
675
676@node_printer('DefElem')
677def def_elem(node, output):
678    output.print_node(node.defname)
679    if node.arg is not Missing:
680        output.write(' = ')
681        if isinstance(node.arg, List):
682            output.write(node.arg.string_value)
683        else:
684            output.print_node(node.arg)
685    if node.defaction != enums.DefElemAction.DEFELEM_UNSPEC:  # pragma: nocover
686        raise NotImplementedError
687
688
689@node_printer('DefineStmt', 'DefElem')
690def define_stmt_def_elem(node, output):
691    output.print_node(node.defname)
692    if node.arg is not Missing:
693        output.write(' = ')
694        if isinstance(node.arg, List):
695            is_symbol = node.defname in ('commutator', 'negator')
696            if is_symbol and len(node.arg) > 1:
697                output.write('OPERATOR(')
698                output.print_symbol(node.arg)
699                output.write(')')
700            else:
701                output.print_symbol(node.arg)
702        else:
703            output.print_node(node.arg)
704    if node.defaction != enums.DefElemAction.DEFELEM_UNSPEC:  # pragma: nocover
705        raise NotImplementedError
706
707
708@node_printer('DropdbStmt')
709def drop_db_stmt(node, output):
710    output.write('DROP DATABASE')
711    if node.missing_ok:
712        output.write(' IF EXISTS')
713    output.write(' ')
714    output.print_name(node.dbname)
715
716
717@node_printer('DropOwnedStmt')
718def drop_owned_stmt(node, output):
719    output.write('DROP OWNED BY ')
720    output.print_list(node.roles, are_names=True)
721    if node.behavior == enums.DropBehavior.DROP_CASCADE:
722        output.write(' CASCADE')
723    elif node.behavior == enums.DropBehavior.DROP_RESTRICT:
724        output.write(' RESTRICT')
725
726
727@node_printer('DropRoleStmt')
728def drop_role_stmt(node, output):
729    output.write('DROP ROLE')
730    if node.missing_ok:
731        output.write(' IF EXISTS')
732    output.write(' ')
733    output.print_list(node.roles, are_names=True)
734
735
736@node_printer('DropStmt')
737def drop_stmt(node, output):
738    otypes = enums.ObjectType
739    output.write('DROP ')
740    output.writes(OBJECT_NAMES[node.removeType.value])
741    if node.removeType == otypes.OBJECT_INDEX:
742        if node.concurrent:
743            output.write(' CONCURRENTLY')
744    if node.missing_ok:
745        output.write(' IF EXISTS')
746    output.write(' ')
747    if node.objects:
748        if node.removeType in (otypes.OBJECT_OPCLASS, otypes.OBJECT_OPFAMILY):
749            nodes = list(node.objects[0])
750            using = nodes.pop(0)
751            output.print_name(nodes)
752            output.write(' USING ')
753            output.print_name(using)
754        elif node.removeType == otypes.OBJECT_TRANSFORM:
755            nodes = list(node.objects[0])
756            output.write('FOR ')
757            output.print_name(nodes.pop(0))
758            output.write(' LANGUAGE ')
759            output.print_name(nodes)
760        elif node.removeType in (otypes.OBJECT_POLICY,
761                                 otypes.OBJECT_RULE,
762                                 otypes.OBJECT_TRIGGER):
763            nodes = list(node.objects[0])
764            on = nodes.pop(0)
765            output.print_name(nodes)
766            output.write(' ON ')
767            output.print_name(on)
768        elif isinstance(node.objects[0], List):
769            if node.objects[0][0].node_tag != 'String':
770                output.print_lists(node.objects, ' AS ', standalone_items=False,
771                                   are_names=True)
772            else:
773                output.print_lists(node.objects, sep='.', sublist_open='', sublist_close='',
774                                   standalone_items=False, are_names=True)
775        else:
776            output.print_list(node.objects, ',', standalone_items=False, are_names=True)
777    if node.behavior == enums.DropBehavior.DROP_CASCADE:
778        output.write(' CASCADE')
779    elif node.behavior == enums.DropBehavior.DROP_RESTRICT:
780        output.write(' RESTRICT')
781
782
783@node_printer('DropSubscriptionStmt')
784def drop_subscription_stmt(node, output):
785    output.write('DROP SUBSCRIPTION')
786    if node.missing_ok:
787        output.write(' IF EXISTS')
788    output.write(' ')
789    output.print_name(node.subname)
790    if node.behavior == enums.DropBehavior.DROP_CASCADE:
791        output.write(' CASCADE')
792    elif node.behavior == enums.DropBehavior.DROP_RESTRICT:
793        output.write(' RESTRICT')
794
795
796@node_printer('DropTableSpaceStmt')
797def drop_table_space_stmt(node, output):
798    output.write('DROP TABLESPACE')
799    if node.missing_ok:
800        output.write(' IF EXISTS')
801    output.write(' ')
802    output.print_name(node.tablespacename)
803
804
805@node_printer('DropUserMappingStmt')
806def drop_user_mapping_stmt(node, output):
807    output.write('DROP USER MAPPING')
808    if node.missing_ok:
809        output.write(' IF EXISTS')
810    output.write(' FOR ')
811    output.print_node(node.user)
812    output.write(' SERVER ')
813    output.print_name(node.servername)
814
815
816@node_printer('FunctionParameter')
817def function_parameter(node, output):
818    if node.mode is not Missing:
819        pm = enums.FunctionParameterMode
820        if node.mode == pm.FUNC_PARAM_IN:
821            pass  # omit the default, output.write('IN ')
822        elif node.mode == pm.FUNC_PARAM_OUT:
823            output.write('OUT ')
824        elif node.mode == pm.FUNC_PARAM_INOUT:
825            output.write('INOUT ')
826        elif node.mode == pm.FUNC_PARAM_VARIADIC:
827            output.write('VARIADIC ')
828        else:
829            raise NotImplementedError
830    if node.name:
831        output.print_name(node.name)
832        output.write(' ')
833    output.print_node(node.argType)
834    if node.defexpr is not Missing:
835        output.write(' = ')
836        output.print_node(node.defexpr)
837
838
839@node_printer('IndexStmt')
840def index_stmt(node, output):
841    output.write('CREATE ')
842    if node.unique:
843        output.write('UNIQUE ')
844    output.write('INDEX ')
845    if node.concurrent:
846        output.write('CONCURRENTLY ')
847    if node.if_not_exists:
848        output.write('IF NOT EXISTS ')
849    if node.idxname:
850        output.print_name(node.idxname)
851    output.newline()
852    with output.push_indent(2):
853        output.write('ON ')
854        output.print_node(node.relation)
855        if node.accessMethod != 'btree':
856            output.write('USING ')
857            output.print_name(node.accessMethod)
858        output.write(' (')
859        output.print_list(node.indexParams)
860        output.write(')')
861        if node.options:
862            output.newline()
863            output.write('WITH (')
864            output.print_list(node.options)
865            output.write(')')
866        if node.tableSpace:
867            output.newline()
868            output.write('TABLESPACE ')
869            output.print_name(node.tableSpace)
870        if node.whereClause:
871            output.newline()
872            output.write('WHERE ')
873            output.print_node(node.whereClause)
874
875
876@node_printer('ObjectWithArgs')
877def object_with_args(node, output):
878    # Special treatment for OPERATOR object inside DROP or COMMENT
879    if (((node.parent_node.removeType or node.parent_node.objtype)
880         == enums.ObjectType.OBJECT_OPERATOR)):
881        output.write(node.objname.string_value)
882        if not node.args_unspecified:
883            output.write(' ')
884    else:
885        output.print_name(node.objname)
886    if not node.args_unspecified:
887        output.write('(')
888        output.print_list(node.objargs, ',', standalone_items=False)
889        output.write(')')
890
891
892@node_printer('PartitionBoundSpec')
893def partition_bound_spec(node, output):
894    if node.strategy.value == enums.PARTITION_STRATEGY_RANGE:
895        output.swrite('FROM (')
896        output.print_list(node.lowerdatums)
897        output.write(') TO (')
898        output.print_list(node.upperdatums)
899        output.write(')')
900    else:
901        output.swrite('IN (')
902        output.print_list(node.listdatums)
903        output.write(')')
904
905
906@node_printer('PartitionElem')
907def partition_elem(node, output):
908    if node.name:
909        output.print_name(node.name)
910    elif node.expr:
911        output.print_node(node.expr)
912    if node.collation or node.opclass:  # pragma: nocover
913        raise NotImplementedError
914
915
916@node_printer('PartitionRangeDatum')
917def partition_range_datum(node, output):
918    if node.kind == enums.PartitionRangeDatumKind.PARTITION_RANGE_DATUM_MINVALUE:
919        output.write('MINVALUE')
920    elif node.kind == enums.PartitionRangeDatumKind.PARTITION_RANGE_DATUM_MAXVALUE:
921        output.write('MAXVALUE')
922    else:
923        output.print_node(node.value)
924
925
926@node_printer('PartitionSpec')
927def partition_spec(node, output):
928    output.print_node(node.strategy)
929    output.write(' (')
930    output.print_list(node.partParams)
931    output.write(')')
932
933
934@node_printer('RoleSpec')
935def role_spec(node, output):
936    if node.roletype == enums.RoleSpecType.ROLESPEC_CURRENT_USER:
937        output.write('CURRENT_USER')
938    elif node.roletype == enums.RoleSpecType.ROLESPEC_SESSION_USER:
939        output.write('SESSION_USER')
940    elif node.roletype == enums.RoleSpecType.ROLESPEC_PUBLIC:
941        output.write('PUBLIC')
942    else:
943        output.print_name(node.rolename)
944