From 85ad5b3693f69ec06afa2f8cdd6147c9def8139b Mon Sep 17 00:00:00 2001 From: Vignesh C Date: Tue, 27 Jun 2023 14:42:03 +0530 Subject: [PATCH 4/5] Auto generate CreateStmt and DropStmt node deparse function for its members. There are three pg_node_attr value that used in the CREATE TABLE deparser code automation. - pg_node_attr(deparse_as(str)) In in case, we can generate all the deparser code for this structure field. The str in the deparse_as function is used as the fmt string if we create a object, or as a json value. This attribute is used for: CreateStmt::tablespacename, CreateStmt::accessMethod and CreateStmt::if_not_exists. - pg_node_attr(deparse_as_special_value) This means we cannot generate the code for the structure field, but we can use this field to check if we need to deparse this field or not. And in gen_node_support.pl, we handle such fields speically by generating different deparsing codes for it (The usage is simialr to existing write_only_relids attr). This is used for CreateStmt::partbound, CreateStmt::ofTypename. The gen_node_support.pl codes that handles these special fields look like: if (elem 'deparse_as_special_value', @a) { print $ddlff "\tif (node->$f)\n"; print $ddlff "\t{\n"; if ($f eq 'partbound') { # generate partboud deparser codes. } elsif ($f eq 'ofTypename') { # generate OF TYPE deparser codes. } print $ddlff "\t}\n\n"; } - pg_node_attr(deparse_as_special_object) Similar to deparse_as_special_value, we cannot generate all the deparser codes for the field. However, since we are creating a sub object here, we can generate the common object creation codes for it, like: auto if (node->options) auto { auto insert_jsonb_key(state, "options"); auto pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL); special new_jsonb_VA(state, 1, "fmt", jbvString, "WITH (%{with:, }s)"); special deparse_withObj_ToJsonb(state, node); auto pushJsonbValue(&state, WJB_END_OBJECT, NULL); auto } For the special part, we need to handle it differently in gen_node_support.pl. Auto generate DropStmt node deparse function for its members. DropStmt node has the following members: objects - No need to auto generate the code for objects member as event trigger will convert the objects list and generate individual object names for each of the members from the list. removeType - No need to auto generated the code for removeType as event trigger will identify the object type for each of the object and pass it to parent deparse function. behavior - Implemented generation of json for this member. missing_ok - Implemented generation of json for this member. concurrent - Implemented generation of json for this member. --- src/backend/nodes/ddldeparsefuncs.c | 174 +++++--------------- src/backend/nodes/gen_node_support.pl | 226 +++++++++++++++++++++++++- src/include/nodes/nodes.h | 2 + src/include/nodes/parsenodes.h | 69 ++++++-- 4 files changed, 317 insertions(+), 154 deletions(-) diff --git a/src/backend/nodes/ddldeparsefuncs.c b/src/backend/nodes/ddldeparsefuncs.c index 9cd42fd7e3..bdded259c7 100644 --- a/src/backend/nodes/ddldeparsefuncs.c +++ b/src/backend/nodes/ddldeparsefuncs.c @@ -48,6 +48,8 @@ extern bool verbose; +#include "ddldeparsefuncs.funcs.c" + /* * Deparse a CreateStmt (CREATE TABLE). * @@ -72,12 +74,14 @@ deparse_create_table(Oid objectId, Node *parsetree) StringInfoData fmtStr; JsonbParseState *state = NULL; JsonbValue *value; + bool telems_present = false; initStringInfo(&fmtStr); /* mark the begin of ROOT object and start adding elements to it. */ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL); + /* Start making fmt string */ appendStringInfoString(&fmtStr, "CREATE"); /* PERSISTENCE */ @@ -93,11 +97,7 @@ deparse_create_table(Oid objectId, Node *parsetree) /* IF NOT EXISTS */ if (node->if_not_exists) - { appendStringInfoString(&fmtStr, " %{if_not_exists}s"); - new_jsonb_VA(state, 1, - "if_not_exists", jbvString, "IF NOT EXISTS"); - } /* IDENTITY */ appendStringInfoString(&fmtStr, " %{identity}D"); @@ -114,100 +114,39 @@ deparse_create_table(Oid objectId, Node *parsetree) * because if there are no options the parentheses must not be emitted; * and also, typed tables do not allow for inheritance. */ - if (node->ofTypename || node->partbound) - { - /* Insert the "of type" or "partition of" clause whichever present */ - if (node->ofTypename) - { - appendStringInfoString(&fmtStr, " OF %{of_type}T"); - new_jsonb_for_type(state, "of_type", - relation->rd_rel->reloftype, -1); - } - else - { - List *parents; - Oid objid; - - appendStringInfoString(&fmtStr, " PARTITION OF %{parent_identity}D"); - parents = deparse_InhRels_ToJsonb(objectId); - objid = linitial_oid(parents); - Assert(list_length(parents) == 1); - new_jsonb_for_qualname_id(state, RelationRelationId, - objid, "parent_identity", true); - } - - /* - * We can't put table elements directly in the fmt string as an array - * surrounded by parentheses here, because an empty clause would cause - * a syntax error. Therefore, we first check the presence and then add - * the elements. - */ - add_table_elems_if_any(state, &fmtStr, relation, - node->tableElts, dpcontext, objectId, - true, /* typed table */ - false); /* not composite */ - } - else - { - List *inhrelations; - bool telems_present = false; - - /* - * There is no need to process LIKE clauses separately; they have - * already been transformed into columns and constraints. - */ + if (node->ofTypename) + appendStringInfoString(&fmtStr, " OF %{of_type}T"); - /* Check if table elements are present, if so, add them. */ - telems_present = add_table_elems_if_any(state, &fmtStr, relation, - node->tableElts, dpcontext, objectId, - false, /* not typed table */ - false); /* not composite */ - - /* - * If no table elements added, then add empty "()" needed for - * 'inherit' create table syntax. Example: CREATE TABLE t1 () INHERITS - * (t0); - */ - if (!telems_present) - appendStringInfoString(&fmtStr, " ()"); - - /* - * Add inheritance specification. We cannot simply scan the list of - * parents from the parser node, because that may lack the actual - * qualified names of the parent relations. Rather than trying to - * re-resolve them from the information in the parse node, it seems - * more accurate and convenient to grab it from pg_inherits. - */ - if (node->inhRelations != NIL) - { - appendStringInfoString(&fmtStr, " %{inherits}s"); - insert_jsonb_key(state, "inherits"); + if (node->partbound) + appendStringInfoString(&fmtStr, " PARTITION OF %{parent_identity}D"); - pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL); + telems_present = add_table_elems_if_any(state, &fmtStr, relation, + node->tableElts, dpcontext, objectId, + node->ofTypename || node->partbound, /* typed table */ + false); /* not composite */ - new_jsonb_VA(state, 1, "fmt", jbvString, "INHERITS (%{parents:, }D)"); - inhrelations = deparse_InhRels_ToJsonb(objectId); + /* + * If no table elements added, then add empty "()" needed for + * 'inherit' create table syntax. Example: CREATE TABLE t1 () INHERITS + * (t0); + */ + if (!node->ofTypename && !node->partbound && !telems_present) + appendStringInfoString(&fmtStr, " ()"); - new_jsonbArray_for_qualname_id(state, "parents", inhrelations); - pushJsonbValue(&state, WJB_END_OBJECT, NULL); - } - } + /* + * Add inheritance specification. We cannot simply scan the list of + * parents from the parser node, because that may lack the actual + * qualified names of the parent relations. Rather than trying to + * re-resolve them from the information in the parse node, it seems + * more accurate and convenient to grab it from pg_inherits. + */ + if (node->inhRelations != NIL && node->partbound == NULL) + appendStringInfoString(&fmtStr, " %{inhRelations}s"); /* FOR VALUES clause */ if (node->partbound) - { appendStringInfoString(&fmtStr, " %{partition_bound}s"); - /* - * Get pg_class.relpartbound. We cannot use partbound in the parsetree - * directly as it's the original partbound expression which haven't - * been transformed. - */ - new_jsonb_VA(state, 1, - "partition_bound", jbvString, - RelationGetPartitionBound(objectId)); - } - /* PARTITION BY clause */ if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { @@ -225,43 +164,17 @@ deparse_create_table(Oid objectId, Node *parsetree) /* USING clause */ if (node->accessMethod) - { - appendStringInfoString(&fmtStr, " %{access_method}s"); - insert_jsonb_key(state, "access_method"); - pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL); - - new_jsonb_VA(state, 2, - "fmt", jbvString, "USING %{access_method}I", - "access_method", jbvString, node->accessMethod); - pushJsonbValue(&state, WJB_END_OBJECT, NULL); - } + appendStringInfoString(&fmtStr, " %{accessMethod}s"); /* WITH clause */ if (node->options) - { - appendStringInfoString(&fmtStr, " %{with_clause}s"); - insert_jsonb_key(state, "with_clause"); - pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL); - new_jsonb_VA(state, 1, - "fmt", jbvString, "WITH (%{with:, }s)"); - - deparse_withObj_ToJsonb(state, node); - - pushJsonbValue(&state, WJB_END_OBJECT, NULL); - - } + appendStringInfoString(&fmtStr, " %{options}s"); /* TABLESPACE */ if (node->tablespacename) - { - appendStringInfoString(&fmtStr, " %{tablespace}s"); - insert_jsonb_key(state, "tablespace"); - pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL); - new_jsonb_VA(state, 2, - "fmt", jbvString, "TABLESPACE %{tablespace}I", - "tablespace", jbvString, node->tablespacename); - pushJsonbValue(&state, WJB_END_OBJECT, NULL); - } + appendStringInfoString(&fmtStr, " %{tablespacename}s"); + + deparse_CreateStmt(state, objectId, relation, parsetree); relation_close(relation, AccessShareLock); @@ -283,7 +196,7 @@ deparse_create_table(Oid objectId, Node *parsetree) * jsonb string representing the drop command. * * Verbose syntax - * DROP %{objtype}s %{concurrently}s %{if_exists}s %{objidentity}s %{cascade}s + * DROP %{objtype}s %{concurrent}s %{missing_ok}s %{objidentity}s %{behavior}s */ char * deparse_drop_table(const char *objidentity, const char *objecttype, @@ -310,18 +223,11 @@ deparse_drop_table(const char *objidentity, const char *objecttype, /* CONCURRENTLY */ if (node->concurrent) - { - appendStringInfoString(&fmtStr, " %{concurrently}s"); - new_jsonb_VA(state, 1, - "concurrently", jbvString, "CONCURRENTLY"); - } + appendStringInfoString(&fmtStr, " %{concurrent}s"); /* IF EXISTS */ if (node->missing_ok) - { - appendStringInfoString(&fmtStr, " %{if_exists}s"); - new_jsonb_VA(state, 1, "if_exists", jbvString, "IF EXISTS"); - } + appendStringInfoString(&fmtStr, " %{missing_ok}s"); /* IDENTITY */ appendStringInfoString(&fmtStr, " %{objidentity}s"); @@ -329,10 +235,10 @@ deparse_drop_table(const char *objidentity, const char *objecttype, /* CASCADE */ if (node->behavior == DROP_CASCADE) - { - appendStringInfoString(&fmtStr, " %{cascade}s"); - new_jsonb_VA(state, 1, "cascade", jbvString, "CASCADE"); - } + appendStringInfoString(&fmtStr, " %{behavior}s"); + + /* Add jsonb values for auto generated fields */ + deparse_DropStmt(state, InvalidOid, NULL, parsetree); /* We have full fmt by now, so add jsonb element for that */ new_jsonb_VA(state, 1, "fmt", jbvString, fmtStr.data); diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl index 72c7963578..04757b79fb 100644 --- a/src/backend/nodes/gen_node_support.pl +++ b/src/backend/nodes/gen_node_support.pl @@ -123,6 +123,8 @@ my @no_copy; my @no_equal; # node types we don't want query jumble support for my @no_query_jumble; +# node types we don't want ddl deparse support for +my @no_ddl_deparse; # node types we don't want read support for my @no_read; # node types we don't want read/write support for @@ -164,6 +166,7 @@ push @node_types, qw(List); push @no_copy, qw(List); push @no_equal, qw(List); push @no_query_jumble, qw(List); +push @no_ddl_deparse, qw(List); push @special_read_write, qw(List); # Nodes with custom copy/equal implementations are skipped from @@ -176,6 +179,9 @@ my @custom_read_write; # Similarly for custom query jumble implementation. my @custom_query_jumble; +# Similarly for custom ddl deparse implementation. +my @custom_ddl_deparse; + # Track node types with manually assigned NodeTag numbers. my %manual_nodetag_number; @@ -329,6 +335,10 @@ foreach my $infile (@ARGV) { push @custom_query_jumble, $in_struct; } + elsif ($attr eq 'custom_ddl_deparse') + { + push @custom_ddl_deparse, $in_struct; + } elsif ($attr eq 'no_copy') { push @no_copy, $in_struct; @@ -436,7 +446,7 @@ foreach my $infile (@ARGV) } # normal struct field elsif ($line =~ - /^\s*(.+)\s*\b(\w+)(\[[\w\s+]+\])?\s*(?:pg_node_attr\(([\w(), ]*)\))?;/ + /^\s*(.+)\s*\b(\w+)(\[[\w\s+]+\])?\s*(?:pg_node_attr\(([\w(%{}\w:), ]*)\))?;/ ) { if ($is_node_struct) @@ -468,6 +478,8 @@ foreach my $infile (@ARGV) if ( $attr !~ /^array_size\(\w+\)$/ && $attr !~ /^copy_as\(\w+\)$/ && $attr !~ /^read_as\(\w+\)$/ + && $attr !~ /^deparse_as\([%\w\s{} ]+\)$/ + && $attr !~ /^deparse_as\(([%\w:{} ]*)\)$/ && !elem $attr, qw(copy_as_scalar equal_as_scalar @@ -476,6 +488,9 @@ foreach my $infile (@ARGV) query_jumble_ignore query_jumble_location read_write_ignore + deparse_ignore + deparse_as_special_value + deparse_as_special_object write_only_relids write_only_nondefault_pathtarget write_only_req_outer)) @@ -494,7 +509,7 @@ foreach my $infile (@ARGV) } # function pointer field elsif ($line =~ - /^\s*([\w\s*]+)\s*\(\*(\w+)\)\s*\((.*)\)\s*(?:pg_node_attr\(([\w(), ]*)\))?;/ + /^\s*([\w\s*]+)\s*\(\*(\w+)\)\s*\((.*)\)\s*(?:pg_node_attr\(([\w(), %]*)\))?;/ ) { if ($is_node_struct) @@ -1339,6 +1354,213 @@ _jumble${n}(JumbleState *jstate, Node *node) close $jff; close $jfs; +# ddldeparsefuncs.c + +push @output_files, 'ddldeparsefuncs.funcs.c'; +open my $ddlff, '>', "$output_path/ddldeparsefuncs.funcs.c$tmpext" or die $!; +push @output_files, 'ddldeparsefuncs.switch.c'; +open my $ddlfs, '>', "$output_path/ddldeparsefuncs.switch.c$tmpext" or die $!; + +printf $ddlff $header_comment, 'ddldeparsefuncs.funcs.c'; +printf $ddlfs $header_comment, 'ddldeparsefuncs.switch.c'; + +print $ddlff $node_includes; + +my @node_ddl_deparse; +push @node_ddl_deparse, "DropStmt"; +push @node_ddl_deparse, "CreateStmt"; + +foreach my $n (@node_ddl_deparse) +{ + next if elem $n, @abstract_types; + next if elem $n, @nodetag_only; + + my $struct_no_ddl_deparse = (elem $n, @no_ddl_deparse); + + if ($n eq 'DropStmt') + { + #print $ddlfs "\t\t\tcase T_${n}:\n" + # . "\t\t\t\tcontext->include_owner = false;\n" + # . "\t\t\t\tjsonb = deparse_${n}(state, parsetree);\n" + # . "\t\t\t\tbreak;\n" + #unless $struct_no_ddl_deparse; + } + else + { + print $ddlfs "\t\t\tcase T_${n}:\n" + . "\t\t\t\tjsonb = deparse_${n}(cmd, context);\n" + . "\t\t\t\tbreak;\n" + unless $struct_no_ddl_deparse; + } + + next if elem $n, @custom_ddl_deparse; + + print $ddlff " +static void +deparse_${n}(JsonbParseState *state, Oid objectId, Relation relation, Node *parsetree) +{ +\t${n} *node = (${n} *) parsetree; + +" unless $struct_no_ddl_deparse; + + # print instructions for each field + foreach my $f (@{ $node_type_info{$n}->{fields} }) + { + my $t = $node_type_info{$n}->{field_types}{$f}; + my @a = @{ $node_type_info{$n}->{field_attrs}{$f} }; + my $ddl_deparse_ignore = $struct_no_ddl_deparse; + + # extract per-field attributes + my $array_size_field; + my $read_as_field1; + my $read_as_field2; + my $deparse_ignore = 0; + foreach my $a (@a) + { + if ($a =~ /^deparse_as\(([\w\s.%{}]+)\)$/) + { + $read_as_field1 = $1; + } + if ($a =~ /^deparse_as\((([\w\s]+):([\w\s]+))\)$/) + { + $read_as_field1 = $2; + $read_as_field2 = $3; + print "field1 = $2\n"; + print "field2 = $3\n"; + } + elsif ($a eq 'deparse_ignore') + { + $ddl_deparse_ignore = 1; + } + } + + if ($ddl_deparse_ignore) + { + # nothing to do if no_read + next if $struct_no_ddl_deparse; + } + + if (elem 'deparse_as_special_value', @a) + { + print $ddlff "\tif (node->$f)\n"; + print $ddlff "\t{\n"; + + if ($f eq 'partbound') + { + print $ddlff "\t\tList *parents = deparse_InhRels_ToJsonb(objectId);\n" + . "\t\tOid objid = linitial_oid(parents);\n" + . "\t\tAssert(list_length(parents) == 1);\n" + . "\t\tnew_jsonb_for_qualname_id(state, RelationRelationId,\n" + . "\t\t\t\t\t\t\t\t objid, \"parent_identity\", true);\n\n" + . "\t\tnew_jsonb_VA(state, 1," + . "\t\t\t\t\t \"partition_bound\", jbvString,\n" + . "\t\t\t\t\t RelationGetPartitionBound(objectId));\n"; + } + elsif ($f eq 'ofTypename') + { + print $ddlff "\t\tnew_jsonb_for_type(state, \"of_type\", relation->rd_rel->reloftype, -1);\n"; + } + + print $ddlff "\t}\n\n"; + + next; + } + elsif (elem 'deparse_as_special_object', @a) + { + if ($f eq 'inhRelations') + { + print $ddlff "\tif (node->$f && !node->partbound)\n"; + } + else + { + print $ddlff "\tif (node->$f)\n"; + } + + print $ddlff "\t{\n"; + print $ddlff "\t\tinsert_jsonb_key(state, \"$f\");\n" + . "\t\tpushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);\n"; + + if ($f eq 'inhRelations') + { + print $ddlff "\t\tnew_jsonb_VA(state, 1, \"fmt\", jbvString, \"INHERITS (%{parents:, }D)\");\n" + . "\t\tnew_jsonbArray_for_qualname_id(state, \"parents\", deparse_InhRels_ToJsonb(objectId));\n"; + } + elsif ($f eq 'options') + { + print $ddlff "\t\tnew_jsonb_VA(state, 1, \"fmt\", jbvString, \"WITH (%{with:, }s)\");\n" + . "\t\tdeparse_withObj_ToJsonb(state, node);\n"; + } + + print $ddlff "\t\tpushJsonbValue(&state, WJB_END_OBJECT, NULL);\n" + . "\t}\n\n"; + next; + } + + # node type + if (($t =~ /^(\w+)\*$/ or $t =~ /^struct\s+(\w+)\*$/) + and elem $1, @node_types) + { + print $ddlff "\tDEPARSE_NODE($f);\n" + unless $ddl_deparse_ignore; + } + elsif ($t eq 'char*') + { + if (defined $read_as_field1 && !$ddl_deparse_ignore) + { + print $ddlff "\tif (node->$f)\n"; + + # if %{} add "fmt" and field name-value + print $ddlff "\t{\n"; + if ($read_as_field1 =~ /\s*%\s*/) + { + print $ddlff "\t\tinsert_jsonb_key(state, \"$f\");\n" + . "\t\tpushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);\n" + . "\t\tnew_jsonb_VA(state, 2,\n" + . "\t\t\t\t\t \"fmt\", jbvString, \"$read_as_field1\",\n" + . "\t\t\t\t\t \"$f\", jbvString, node->$f);\n" + . "\t\tpushJsonbValue(&state, WJB_END_OBJECT, NULL);\n"; + } + else + { + print $ddlff "\t\tnew_jsonb_VA\(state, 1, \"$f\", jbvString, node->$f ? \"$read_as_field1\" : \"\"\);\n"; + } + print $ddlff "\t}\n\n"; + } + } + elsif ($t eq 'bool') + { + if (defined $read_as_field1) + { + print $ddlff "\tif (node->$f)\n" + unless $ddl_deparse_ignore; + print $ddlff "\t\tnew_jsonb_VA\(state, 1, \"$f\", jbvString, \"$read_as_field1\"\);\n\n" + unless $ddl_deparse_ignore; + } + } + elsif (elem $t, @enum_types) + { + print $ddlff "\tif (node->behavior == $read_as_field1)\n" + unless $ddl_deparse_ignore; + print $ddlff "\t\tnew_jsonb_VA\(state, 1, \"$f\", jbvString, \"$read_as_field2\");\n\n" + unless $ddl_deparse_ignore; + } + } + + # Some nodes have no attributes like CheckPointStmt, + # so tweak things for empty contents. + if (scalar(@{ $node_type_info{$n}->{fields} }) == 0) + { + print $ddlff "\t(void) expr;\n" + unless $struct_no_ddl_deparse; + } + + print $ddlff "} +" unless $struct_no_ddl_deparse; +} + +close $ddlff; +close $ddlfs; + # now rename the temporary files to their final names foreach my $file (@output_files) { diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index f8e8fe699a..d049ce91ff 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -55,6 +55,8 @@ typedef enum NodeTag * * - custom_query_jumble: Has custom implementation in queryjumblefuncs.c. * + * - custom_ddl_deparse: Has custom implementation in ddldeparsefuncs.c. + * * - no_copy: Does not support copyObject() at all. * * - no_equal: Does not support equal() at all. diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index b8ab8dae6c..86532a3f02 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2472,19 +2472,42 @@ typedef struct VariableShowStmt typedef struct CreateStmt { NodeTag type; - RangeVar *relation; /* relation to create */ - List *tableElts; /* column definitions (list of ColumnDef) */ - List *inhRelations; /* relations to inherit from (list of - * RangeVar) */ - PartitionBoundSpec *partbound; /* FOR VALUES clause */ - PartitionSpec *partspec; /* PARTITION BY clause */ - TypeName *ofTypename; /* OF typename */ - List *constraints; /* constraints (list of Constraint nodes) */ - List *options; /* options from WITH clause */ - OnCommitAction oncommit; /* what do we do at COMMIT? */ - char *tablespacename; /* table space to use, or NULL */ - char *accessMethod; /* table access method */ - bool if_not_exists; /* just do nothing if it already exists? */ + + /* relation to create */ + RangeVar *relation pg_node_attr(deparse_ignore); + + /* column definitions (list of ColumnDef) */ + List *tableElts pg_node_attr(deparse_ignore); + + /* relations to inherit from (list of RangeVar) */ + List *inhRelations pg_node_attr(deparse_as_special_object); + + /* FOR VALUES clause */ + PartitionBoundSpec *partbound pg_node_attr(deparse_as_special_value); + + /* PARTITION BY clause */ + PartitionSpec *partspec pg_node_attr(deparse_ignore); + + /* OF typename */ + TypeName *ofTypename pg_node_attr(deparse_as_special_value); + + /* constraints (list of Constraint nodes) */ + List *constraints pg_node_attr(deparse_ignore); + + /* options from WITH clause */ + List *options pg_node_attr(deparse_as_special_object); + + /* what do we do at COMMIT? */ + OnCommitAction oncommit pg_node_attr(deparse_ignore); + + /* table space to use, or NULL */ + char *tablespacename pg_node_attr(deparse_as(TABLESPACE %{tablespacename}I)); + + /* table access method */ + char *accessMethod pg_node_attr(deparse_as(USING %{accessMethod}I)); + + /* just do nothing if it already exists? */ + bool if_not_exists pg_node_attr(deparse_as(IF NOT EXISTS)); } CreateStmt; /* ---------- @@ -3059,11 +3082,21 @@ typedef struct AlterOpFamilyStmt typedef struct DropStmt { NodeTag type; - List *objects; /* list of names */ - ObjectType removeType; /* object type */ - DropBehavior behavior; /* RESTRICT or CASCADE behavior */ - bool missing_ok; /* skip error if object is missing? */ - bool concurrent; /* drop index concurrently? */ + + /* list of names */ + List *objects pg_node_attr(deparse_ignore); + + /* object type */ + ObjectType removeType pg_node_attr(deparse_ignore); + + /* RESTRICT or CASCADE behavior */ + DropBehavior behavior pg_node_attr(deparse_as(DROP_CASCADE:CASCADE)); + + /* skip error if object is missing? */ + bool missing_ok pg_node_attr(deparse_as(IF EXISTS)); + + /* drop index concurrently? */ + bool concurrent pg_node_attr(deparse_as(CONCURRENTLY)); } DropStmt; /* ---------------------- -- 2.34.1