0008-Create-table-object-tree-removal-2023_05_09.patch
application/octet-stream
Filename: 0008-Create-table-object-tree-removal-2023_05_09.patch
Type: application/octet-stream
Part: 7
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0008
Subject: create table object tree removal
| File | + | − |
|---|---|---|
| src/backend/commands/ddldeparse.c | 1404 | 8 |
From a3bbf2c35d16885d3266e01310d504132eadee2f Mon Sep 17 00:00:00 2001
From: Shveta Malik <shveta.malik@gmail.com>
Date: Mon, 8 May 2023 15:37:43 +0530
Subject: [PATCH 8/8] create table object tree removal
---
src/backend/commands/ddldeparse.c | 1412 ++++++++++++++++++++++++++++-
1 file changed, 1404 insertions(+), 8 deletions(-)
diff --git a/src/backend/commands/ddldeparse.c b/src/backend/commands/ddldeparse.c
index 5878d7e465..7920f1d0a6 100644
--- a/src/backend/commands/ddldeparse.c
+++ b/src/backend/commands/ddldeparse.c
@@ -160,6 +160,36 @@ static List *deparse_TableElements(Relation relation, List *tableElements, List
bool typed, bool composite);
static void mark_function_volatile(ddl_deparse_context *context, Node *expr);
+static void deparse_ColumnDef_toJsonb(JsonbParseState **state,
+ Relation relation, List *dpcontext,
+ bool composite, ColumnDef *coldef,
+ bool is_alter, Node **expr,
+ bool *belemsAdded);
+static void deparse_TableElementsToJsonb(JsonbParseState **state,
+ Relation relation, List *tableElements,
+ List *dpcontext, bool typed,
+ bool composite, bool *belemsAdded);
+
+static void insert_jsonb_key(JsonbParseState *state, char *name);
+
+static void insert_jsonb_str_element(JsonbParseState *state, char *name, char *value);
+
+static void insert_jsonb_bool_element(JsonbParseState *state, char *name, bool value);
+
+static void insert_str_object(JsonbParseState *state, char *fmt, char *key, char *val);
+
+static void insert_not_present_obj(JsonbParseState *state, char *fmt);
+
+static void fmt_to_jsonb_element(JsonbParseState *state, char *fmtStr);
+
+static void new_jsonb_for_type(JsonbParseState *state, Oid typeId, int32 typmod);
+
+static void new_jsonb_for_qualname(JsonbParseState *state, Oid nspid, char *objName, char* keyName);
+
+static void new_jsonb_for_qualname_id(JsonbParseState *state, Oid classId, Oid objectId, char* keyName);
+
+static JsonbValue *parsetree_to_jsonb_create_table(Oid objectId, Node *parsetree, char *owner);
+
/*
* Mark the func_volatile flag for an expression in the command.
*/
@@ -3154,7 +3184,7 @@ deparse_RenameStmt(ObjectAddress address, Node *parsetree)
* This function should cover all cases handled in ProcessUtilitySlow.
*/
static ObjTree *
-deparse_simple_command(CollectedCommand *cmd, bool *include_owner)
+deparse_simple_command(CollectedCommand *cmd, ddl_deparse_context *context)
{
Oid objectId;
Node *parsetree;
@@ -3171,13 +3201,13 @@ deparse_simple_command(CollectedCommand *cmd, bool *include_owner)
switch (nodeTag(parsetree))
{
case T_AlterObjectSchemaStmt:
- *include_owner = false;
+ context->include_owner = false;
return deparse_AlterObjectSchemaStmt(cmd->d.simple.address,
parsetree,
cmd->d.simple.secondaryObject);
case T_AlterOwnerStmt:
- *include_owner = false;
+ context->include_owner = false;
return deparse_AlterOwnerStmt(cmd->d.simple.address, parsetree);
case T_AlterSeqStmt:
@@ -3187,10 +3217,21 @@ deparse_simple_command(CollectedCommand *cmd, bool *include_owner)
return deparse_CreateSeqStmt(objectId, parsetree);
case T_CreateStmt:
- return deparse_CreateStmt(objectId, parsetree);
+ JsonbValue *value;
+
+ /* get direct jsonbValue object from parsetree */
+ value = context->include_owner ?
+ parsetree_to_jsonb_create_table(objectId, parsetree, cmd->role) :
+ parsetree_to_jsonb_create_table(objectId, parsetree, NULL);
+
+ /*
+ * we are returning jsonb*, but typecast to ObjTree* for the
+ * time being to satisfy the prototype
+ */
+ return (ObjTree *)JsonbValueToJsonb(value);
case T_RenameStmt:
- *include_owner = false;
+ context->include_owner = false;
return deparse_RenameStmt(cmd->d.simple.address, parsetree);
default:
@@ -3246,7 +3287,7 @@ deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context *context)
switch (cmd->type)
{
case SCT_Simple:
- tree = deparse_simple_command(cmd, &context->include_owner);
+ tree = deparse_simple_command(cmd, context);
break;
case SCT_AlterTable:
tree = deparse_AlterRelation(cmd, context);
@@ -3265,9 +3306,18 @@ deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context *context)
{
Jsonb *jsonb;
- jsonb = context->include_owner ? objtree_to_jsonb(tree, cmd->role) :
+ if (nodeTag(cmd->parsetree) == T_CreateStmt)
+ {
+ /* no object tree as intermediate stage for Create-Table */
+ jsonb = (Jsonb *)tree;
+ command = JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
+ }
+ else
+ {
+ jsonb = context->include_owner ? objtree_to_jsonb(tree, cmd->role) :
objtree_to_jsonb(tree, NULL);
- command = JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
+ command = JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
+ }
}
/*
@@ -3304,3 +3354,1349 @@ ddl_deparse_to_json(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
}
+
+/*
+ * Insert JsonbValue key to JsonbParseState
+ */
+static void
+insert_jsonb_key(JsonbParseState *state, char *name)
+{
+ JsonbValue key;
+
+ /* Push the key */
+ key.type = jbvString;
+ key.val.string.val = name;
+ key.val.string.len = strlen(name);
+ pushJsonbValue(&state, WJB_KEY, &key);
+}
+
+/*
+ * Insert JsonbValue key:value pair to JsonbParseState, the
+ * value here is of type jbvString.
+ */
+static void
+insert_jsonb_str_element(JsonbParseState *state, char *name, char *value)
+{
+ JsonbValue val;
+
+ /* Push the key first */
+ insert_jsonb_key(state, name);
+
+ /* Push the value now */
+ val.type = jbvString;
+ val.val.string.len = strlen(value);
+ val.val.string.val = pstrdup(value);
+ pushJsonbValue(&state, WJB_VALUE, &val);
+}
+
+/*
+ * Insert JsonbValue key:value pair to JsonbParseState, the
+ * value here is of type jbvBool.
+ */
+static void
+insert_jsonb_bool_element(JsonbParseState *state, char *name, bool value)
+{
+ JsonbValue val;
+
+ /* Push the key first */
+ insert_jsonb_key(state, name);
+
+ /* Push the value now */
+ val.type = jbvBool;
+ val.val.boolean = value;
+ pushJsonbValue(&state, WJB_VALUE, &val);
+}
+
+/*
+ * Insert JsonbValue key and the jsonb array to JsonbParseState
+ */
+static void
+insert_jsonb_array_oid(JsonbParseState *state, char *keyname, List *array)
+{
+ ListCell *lc;
+
+ /* Push the key first */
+ insert_jsonb_key(state, keyname);
+
+ pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
+ /* Push the array elements now */
+ foreach(lc, array)
+ new_jsonb_for_qualname_id(state, RelationRelationId, lfirst_oid(lc),
+ NULL);
+
+ pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+}
+
+/*
+ * Insert new Object with one key:value pair to JsonbParseState, the
+ * value here is of jbvString.
+ */
+static void
+insert_str_object(JsonbParseState *state, char *fmt, char *keynm, char *keyval)
+{
+
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+ fmt_to_jsonb_element(state, fmt);
+
+ /* push key-value pair */
+ insert_jsonb_str_element(state, keynm, keyval);
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+
+/*
+ * Insert new object with present:false as key-value pair
+ * to JsonbParseState
+ */
+static void
+insert_not_present_obj(JsonbParseState *state, char *fmt)
+{
+
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+ fmt_to_jsonb_element(state, fmt);
+
+ /* push key-value pair */
+ insert_jsonb_bool_element(state, "present", false);
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Insert the format string into the output parse state.
+ */
+static void
+fmt_to_jsonb_element(JsonbParseState *state, char *fmtStr)
+{
+ Assert(fmtStr);
+ insert_jsonb_str_element(state, "fmt", fmtStr);
+}
+
+/*
+ * A helper routine to insert jsonb for coltyp to JsonbParseState
+ */
+static void
+new_jsonb_for_type(JsonbParseState *state, Oid typeId, int32 typmod)
+{
+ Oid typnspid;
+ char *type_nsp;
+ char *type_name = NULL;
+ char *typmodstr;
+ bool type_array;
+
+ format_type_detailed(typeId, typmod,
+ &typnspid, &type_name, &typmodstr, &type_array);
+
+ if (OidIsValid(typnspid))
+ type_nsp = get_namespace_name_or_temp(typnspid);
+ else
+ type_nsp = pstrdup("");
+
+ /* create object now for its value */
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+ /* coltype object will have 4 elements */
+ insert_jsonb_str_element(state, "schemaname", type_nsp);
+ insert_jsonb_str_element(state, "typename", type_name);
+ insert_jsonb_str_element(state, "typmod", typmodstr);
+ insert_jsonb_bool_element(state, "typarray", type_array);
+
+ /* mark end of object */
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * A helper routine to set up name: schemaname, objname
+ *
+ * Elements "schema_name" and "obj_name" are set. If the namespace OID
+ * corresponds to a temp schema, that's set to "pg_temp".
+ *
+ * The difference between those two element types is whether the obj_name will
+ * be quoted as an identifier or not, which is not something that this routine
+ * concerns itself with; that will be up to the expand function.
+ */
+static void
+new_jsonb_for_qualname(JsonbParseState *state, Oid nspid, char *objName, char *keyName)
+{
+ char *namespace;
+
+ if (isAnyTempNamespace(nspid))
+ namespace = pstrdup("pg_temp");
+ else
+ namespace = get_namespace_name(nspid);
+
+ /* Push the key first */
+ if (keyName)
+ insert_jsonb_key(state, keyName);
+
+ /* create object now for its value */
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+ /* Add schemaname and objname now */
+ insert_jsonb_str_element(state, "schemaname", namespace);
+ insert_jsonb_str_element(state, "objname", objName);
+
+ /* mark end of object */
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * A helper routine to set up name: 'schemaname, objname' where the object is
+ * specified by classId and objId.
+ */
+static void
+new_jsonb_for_qualname_id(JsonbParseState *state, Oid classId, Oid objectId, char *keyName)
+{
+ Relation catalog;
+ HeapTuple catobj;
+ Datum obj_nsp;
+ Datum obj_name;
+ AttrNumber Anum_name;
+ AttrNumber Anum_namespace;
+ AttrNumber Anum_oid = get_object_attnum_oid(classId);
+ bool isnull;
+
+ catalog = table_open(classId, AccessShareLock);
+
+ catobj = get_catalog_object_by_oid(catalog, Anum_oid, objectId);
+ if (!catobj)
+ elog(ERROR, "cache lookup failed for object with OID %u of catalog \"%s\"",
+ objectId, RelationGetRelationName(catalog));
+ Anum_name = get_object_attnum_name(classId);
+ Anum_namespace = get_object_attnum_namespace(classId);
+
+ obj_nsp = heap_getattr(catobj, Anum_namespace, RelationGetDescr(catalog),
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null namespace for object %u", objectId);
+
+ obj_name = heap_getattr(catobj, Anum_name, RelationGetDescr(catalog),
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null attribute name for object %u", objectId);
+
+ new_jsonb_for_qualname(state, DatumGetObjectId(obj_nsp),
+ NameStr(*DatumGetName(obj_name)), keyName);
+ table_close(catalog, AccessShareLock);
+}
+
+/*
+ * A helper function to insert collate object for column definition
+ */
+static void
+insert_collate_object(JsonbParseState *state, char *fmt, Oid classId, Oid objectId)
+{
+
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+ fmt_to_jsonb_element(state, fmt);
+
+ /* push object now */
+ new_jsonb_for_qualname_id(state, classId, objectId, "collation_name");
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * A helper function to insert identity object for the table definition
+ */
+static void
+insert_identity_object(JsonbParseState *state, Oid nspid, char *relname)
+{
+ char *namespace;
+
+ if (isAnyTempNamespace(nspid))
+ namespace = pstrdup("pg_temp");
+ else
+ namespace = get_namespace_name(nspid);
+
+ /* Push the key first */
+ insert_jsonb_key(state, "identity");
+
+ /* create object now for its value */
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+ /* identity object will have 2 elements */
+ insert_jsonb_str_element(state, "schemaname", namespace);
+ insert_jsonb_str_element(state, "objname", relname);
+
+ /* mark end of object */
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Deparse the sequence CACHE option to Jsonb
+ *
+ * Verbose syntax
+ * SET CACHE %{value}s
+ * OR
+ * CACHE %{value}
+ */
+static inline void
+deparse_Seq_Cache_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
+{
+ char *fmt;
+
+ fmt = alter_table ? "SET CACHE %{value}s" : "CACHE %{value}s";
+
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(state, fmt);
+
+ insert_jsonb_str_element(state, "clause", "cache");
+ insert_jsonb_str_element(state, "value", psprintf(INT64_FORMAT, seqdata->seqcache));
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Deparse the sequence CYCLE option to Jsonb.
+ *
+ * Verbose syntax
+ * SET %{no}s CYCLE
+ * OR
+ * %{no}s CYCLE
+ */
+static inline void
+deparse_Seq_Cycle_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
+{
+ char *fmt;
+
+ fmt = alter_table ? "SET %{no}s CYCLE" : "%{no}s CYCLE";
+
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(state, fmt);
+
+ insert_jsonb_str_element(state, "clause", "cycle");
+ insert_jsonb_str_element(state, "no", seqdata->seqcycle ? "" : "NO");
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Deparse the sequence INCREMENT BY option to Jsonb
+ *
+ * Verbose syntax
+ * SET INCREMENT BY %{value}s
+ * OR
+ * INCREMENT BY %{value}s
+ */
+static inline void
+deparse_Seq_IncrementBy_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
+{
+ char *fmt;
+
+ fmt = alter_table ? "SET INCREMENT BY %{value}s" : "INCREMENT BY %{value}s";
+
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(state, fmt);
+
+ insert_jsonb_str_element(state, "clause", "seqincrement");
+ insert_jsonb_str_element(state, "value", psprintf(INT64_FORMAT, seqdata->seqincrement));
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Deparse the sequence MAXVALUE option to Jsonb.
+ *
+ * Verbose syntax
+ * SET MAXVALUE %{value}s
+ * OR
+ * MAXVALUE %{value}s
+ */
+static inline void
+deparse_Seq_Maxvalue_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
+{
+ char *fmt;
+
+ fmt = alter_table ? "SET MAXVALUE %{value}s" : "MAXVALUE %{value}s";
+
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(state, fmt);
+
+ insert_jsonb_str_element(state, "clause", "maxvalue");
+ insert_jsonb_str_element(state, "value", psprintf(INT64_FORMAT, seqdata->seqmax));
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Deparse the sequence MINVALUE option to Jsonb
+ *
+ * Verbose syntax
+ * SET MINVALUE %{value}s
+ * OR
+ * MINVALUE %{value}s
+ */
+static inline void
+deparse_Seq_Minvalue_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
+{
+ char *fmt;
+
+ fmt = alter_table ? "SET MINVALUE %{value}s" : "MINVALUE %{value}s";
+
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(state, fmt);
+
+ insert_jsonb_str_element(state, "clause", "minvalue");
+ insert_jsonb_str_element(state, "value", psprintf(INT64_FORMAT, seqdata->seqmin));
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Deparse the sequence START WITH option to Jsonb.
+ *
+ * Verbose syntax
+ * SET START WITH %{value}s
+ * OR
+ * START WITH %{value}s
+ */
+static inline void
+deparse_Seq_Startwith_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
+{
+ char *fmt;
+
+ fmt = alter_table ? "SET START WITH %{value}s" : "START WITH %{value}s";
+
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(state, fmt);
+
+ insert_jsonb_str_element(state, "clause", "start");
+ insert_jsonb_str_element(state, "value", psprintf(INT64_FORMAT, seqdata->seqstart));
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Deparse the sequence RESTART option to Jsonb
+ *
+ * Verbose syntax
+ * RESTART %{value}s
+ */
+static inline void
+deparse_Seq_Restart_toJsonb(JsonbParseState *state, int64 last_value)
+{
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(state, "RESTART %{value}s");
+
+ insert_jsonb_str_element(state, "clause", "restart");
+ insert_jsonb_str_element(state, "value", psprintf(INT64_FORMAT, last_value));
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+
+/*
+ * Deparse the definition of column identity to Jsonb.
+ *
+ * Verbose syntax
+ * SET GENERATED %{option}s %{identity_type}s %{seq_definition: }s
+ * OR
+ * GENERATED %{option}s AS IDENTITY %{identity_type}s ( %{seq_definition: }s )
+ */
+static void
+deparse_ColumnIdentity_toJsonb(JsonbParseState *state, Oid seqrelid, char identity, bool alter_table)
+{
+ Form_pg_sequence seqform;
+ Sequence_values *seqvalues;
+ char *fmt;
+
+ if (alter_table)
+ fmt = "%{identity_type}s %{seq_definition: }s";
+ else
+ fmt = "%{identity_type}s ( %{seq_definition: }s )";
+
+ /* create object now for value of identity_column */
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(state, fmt);
+
+ /* identity_type object creation */
+ {
+ char *identfmt;
+
+ /* Push the key first */
+ insert_jsonb_key(state, "identity_type");
+
+ if (alter_table)
+ identfmt = "SET GENERATED %{option}s";
+ else
+ identfmt = "GENERATED %{option}s AS IDENTITY";
+
+ if (identity == ATTRIBUTE_IDENTITY_ALWAYS)
+ insert_str_object(state, identfmt, "option", "ALWAYS");
+ else if (identity == ATTRIBUTE_IDENTITY_BY_DEFAULT)
+ insert_str_object(state, identfmt, "option", "BY DEFAULT");
+ else
+ insert_not_present_obj(state, verbose ? identfmt : (alter_table ? "SET GENERATED " : "GENERATED "));
+ }
+
+ /* seq_definition array object creation */
+ {
+
+ /* Push the key first */
+ insert_jsonb_key(state, "seq_definition");
+
+ pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
+ seqvalues = get_sequence_values(seqrelid);
+ seqform = seqvalues->seqform;
+
+ /* Definition elements */
+ deparse_Seq_Cache_toJsonb(state, seqform, alter_table);
+ deparse_Seq_Cycle_toJsonb(state, seqform, alter_table);
+ deparse_Seq_IncrementBy_toJsonb(state, seqform, alter_table);
+ deparse_Seq_Minvalue_toJsonb(state, seqform, alter_table);
+ deparse_Seq_Maxvalue_toJsonb(state, seqform, alter_table);
+ deparse_Seq_Startwith_toJsonb(state, seqform, alter_table);
+ deparse_Seq_Restart_toJsonb(state, seqvalues->last_value);
+ /* We purposefully do not emit OWNED BY here */
+
+ pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+ }
+
+ /* end of idendity_column object */
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+
+/*
+ * Deparse a ColumnDef node within a regular (non-typed) table creation.
+ *
+ * NOT NULL constraints in the column definition are emitted directly in the
+ * column definition by this routine; other constraints must be emitted
+ * elsewhere (the info in the parse node is incomplete anyway).
+ *
+ * Verbose syntax
+ * "%{name}I %{coltype}T STORAGE %{colstorage}s %{compression}s %{collation}s
+ * %{not_null}s %{default}s %{identity_column}s %{generated_column}s"
+ */
+static void
+deparse_ColumnDef_toJsonb(JsonbParseState **state, Relation relation,
+ List *dpcontext, bool composite, ColumnDef *coldef,
+ bool is_alter, Node **expr, bool *belemsAdded)
+{
+ Oid relid = RelationGetRelid(relation);
+ HeapTuple attrTup;
+ Form_pg_attribute attrForm;
+ Oid typid;
+ int32 typmod;
+ Oid typcollation;
+ char *fmtStr = "%{name}I %{coltype}T STORAGE %{colstorage}s %{compression}s %{collation}s "
+ "%{not_null}s %{default}s %{identity_column}s %{generated_column}s";
+ bool saw_notnull;
+ ListCell *cell;
+
+ /*
+ * Inherited columns without local definitions must not be emitted.
+ *
+ * XXX maybe it is useful to have them with "present = false" or some
+ * such?
+ */
+ if (!coldef->is_local)
+ return;
+
+ attrTup = SearchSysCacheAttName(relid, coldef->colname);
+ if (!HeapTupleIsValid(attrTup))
+ elog(ERROR, "could not find cache entry for column \"%s\" of relation %u",
+ coldef->colname, relid);
+ attrForm = (Form_pg_attribute) GETSTRUCT(attrTup);
+
+ get_atttypetypmodcoll(relid, attrForm->attnum,
+ &typid, &typmod, &typcollation);
+
+ if (!*belemsAdded)
+ {
+ *belemsAdded = true;
+ insert_jsonb_key(*state, "table_elements");
+ pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
+
+ fmt_to_jsonb_element(*state, "(%{elements:, }s)");
+
+ insert_jsonb_key(*state, "elements");
+ pushJsonbValue(state, WJB_BEGIN_ARRAY, NULL);
+ }
+
+ /* start making column object */
+ pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(*state, fmtStr);
+
+ /* creat name and type elements for column */
+ insert_jsonb_str_element(*state, "name", coldef->colname);
+ insert_jsonb_str_element(*state, "type", "column");
+
+ /*
+ * create coltype object having 4 elements: schemaname, typename, typemod,
+ * typearray
+ */
+ {
+ /* Push the key first */
+ insert_jsonb_key(*state, "coltype");
+
+ /* Push the value */
+ new_jsonb_for_type(*state, typid, typmod);
+ }
+
+ /* STORAGE clause */
+ if (!composite)
+ insert_jsonb_str_element(*state, "colstorage", storage_name(attrForm->attstorage));
+
+ /* COMPRESSION clause */
+ {
+ /* Push the key first */
+ insert_jsonb_key(*state, "compression");
+
+ /* Push the value now: object in this case */
+ if (coldef->compression)
+ insert_str_object(*state, "COMPRESSION %{compression_method}I",
+ "compression_method", coldef->compression);
+ else
+ insert_not_present_obj(*state, verbose ? "COMPRESSION %{compression_method}I" : "COMPRESSION");
+ }
+
+ /* COLLATE clause */
+ {
+ /* Push the key first */
+ insert_jsonb_key(*state, "collation");
+
+ if (OidIsValid(typcollation))
+ insert_collate_object(*state, "COLLATE %{collation_name}D", CollationRelationId, typcollation);
+ else
+ insert_not_present_obj(*state, verbose ? "COLLATE %{collation_name}D" : "COLLATE");
+ }
+
+ if (!composite)
+ {
+ Oid seqrelid = InvalidOid;
+
+ /*
+ * Emit a NOT NULL declaration if necessary. Note that we cannot
+ * trust pg_attribute.attnotnull here, because that bit is also set
+ * when primary keys are specified; we must not emit a NOT NULL
+ * constraint in that case, unless explicitly specified. Therefore,
+ * we scan the list of constraints attached to this column to
+ * determine whether we need to emit anything. (Fortunately, NOT NULL
+ * constraints cannot be table constraints.)
+ *
+ * In the ALTER TABLE cases, we also add a NOT NULL if the colDef is
+ * marked is_not_null.
+ */
+ saw_notnull = false;
+ foreach(cell, coldef->constraints)
+ {
+ Constraint *constr = (Constraint *) lfirst(cell);
+
+ if (constr->contype == CONSTR_NOTNULL)
+ {
+ saw_notnull = true;
+ break;
+ }
+ }
+
+ if (is_alter && coldef->is_not_null)
+ saw_notnull = true;
+
+ /* NOT NULL */
+ insert_jsonb_str_element(*state, "not_null", saw_notnull ? "NOT NULL" : "");
+
+ /* DEFAULT: Push the key first */
+ insert_jsonb_key(*state, "default");
+
+ /* DEFAULT: Push the value now */
+ if (attrForm->atthasdef &&
+ coldef->generated != ATTRIBUTE_GENERATED_STORED)
+ {
+ char *defstr;
+
+ defstr = RelationGetColumnDefault(relation, attrForm->attnum,
+ dpcontext, expr);
+
+ insert_str_object(*state, "DEFAULT %{default}s", "default", defstr);
+ }
+ else
+ insert_not_present_obj(*state, verbose ? "DEFAULT %{default}s" : "DEFAULT");
+
+
+ /* IDENTITY COLUMN */
+ if (coldef->identity)
+ {
+ /*
+ * For identity column, find the sequence owned by column in order
+ * to deparse the column definition.
+ */
+ seqrelid = getIdentitySequence(relid, attrForm->attnum, true);
+ if (OidIsValid(seqrelid) && coldef->identitySequence)
+ seqrelid = RangeVarGetRelid(coldef->identitySequence, NoLock, false);
+ }
+
+ /* IDENTITY COLUMN: Push the key first */
+ insert_jsonb_key(*state, "identity_column");
+
+ /* IDENTITY COLUMN: Push the value now */
+ if (OidIsValid(seqrelid))
+ {
+
+ deparse_ColumnIdentity_toJsonb(*state, seqrelid, coldef->identity, is_alter);
+ }
+ else
+ insert_not_present_obj(*state, verbose ? "%{identity_column}s" : "");
+
+ /* GENERATED COLUMN EXPRESSION: Push the key first */
+ insert_jsonb_key(*state, "generated_column");
+
+ /* GENERATED COLUMN EXPRESSION: Push the value now */
+ if (coldef->generated == ATTRIBUTE_GENERATED_STORED)
+ {
+ char *defstr;
+
+ defstr = RelationGetColumnDefault(relation, attrForm->attnum,
+ dpcontext, expr);
+ insert_str_object(*state, "GENERATED ALWAYS AS (%{generation_expr}s) STORED", "generation_expr", defstr);
+ }
+ else
+ insert_not_present_obj(*state, verbose ? "GENERATED ALWAYS AS (%{generation_expr}s) STORED" : "GENERATED ALWAYS AS");
+ }
+ ReleaseSysCache(attrTup);
+
+ /* mark the end of one column object */
+ pushJsonbValue(state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Deparse a ColumnDef node within a typed table creation. This is simpler
+ * than the regular case, because we don't have to emit the type declaration,
+ * collation, or default. Here we only return something if the column is being
+ * declared NOT NULL.
+ *
+ * As in deparse_ColumnDef, any other constraint is processed elsewhere.
+ *
+ * Verbose syntax
+ * %{name}I WITH OPTIONS %{not_null}s %{default}s.
+ */
+static void
+deparse_ColumnDef_typed_toJsonb(JsonbParseState **state, Relation relation,
+ List *dpcontext, ColumnDef *coldef,
+ bool *belemsAdded)
+{
+ Oid relid = RelationGetRelid(relation);
+ HeapTuple attrTup;
+ Form_pg_attribute attrForm;
+ Oid typid;
+ int32 typmod;
+ Oid typcollation;
+ bool saw_notnull;
+ ListCell *cell;
+ char *fmtStr = "%{name}I WITH OPTIONS %{not_null}s %{default}s";
+
+ attrTup = SearchSysCacheAttName(relid, coldef->colname);
+ if (!HeapTupleIsValid(attrTup))
+ elog(ERROR, "could not find cache entry for column \"%s\" of relation %u",
+ coldef->colname, relid);
+ attrForm = (Form_pg_attribute) GETSTRUCT(attrTup);
+
+ get_atttypetypmodcoll(relid, attrForm->attnum,
+ &typid, &typmod, &typcollation);
+
+ /*
+ * Search for a NOT NULL declaration. As in deparse_ColumnDef, we rely on
+ * finding a constraint on the column rather than coldef->is_not_null.
+ * (This routine is never used for ALTER cases.)
+ */
+ saw_notnull = false;
+ foreach(cell, coldef->constraints)
+ {
+ Constraint *constr = (Constraint *) lfirst(cell);
+
+ if (constr->contype == CONSTR_NOTNULL)
+ {
+ saw_notnull = true;
+ break;
+ }
+ }
+
+ if (!saw_notnull && !attrForm->atthasdef)
+ {
+ ReleaseSysCache(attrTup);
+ return;
+ }
+
+ if (!*belemsAdded)
+ {
+ *belemsAdded = true;
+ insert_jsonb_key(*state, "table_elements");
+ pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
+
+ fmt_to_jsonb_element(*state, "(%{elements:, }s)");
+
+ insert_jsonb_key(*state, "elements");
+ pushJsonbValue(state, WJB_BEGIN_ARRAY, NULL);
+ }
+
+ /* start making column object */
+ pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(*state, fmtStr);
+
+ /* Insert TYPE, NAME and NOT_NULL elements */
+ insert_jsonb_str_element(*state, "type", "column");
+ insert_jsonb_str_element(*state, "name", coldef->colname);
+ insert_jsonb_str_element(*state, "not_null", saw_notnull ? "NOT NULL" : "");
+
+ /* DEFAULT: Push the key first */
+ insert_jsonb_key(*state, "default");
+
+ /* DEFAULT: Push the value now */
+ if (attrForm->atthasdef)
+ {
+ char *defstr;
+
+ defstr = RelationGetColumnDefault(relation, attrForm->attnum,
+ dpcontext, NULL);
+ insert_str_object(*state, "DEFAULT %{default}s", "default", defstr);
+ }
+ else
+ insert_not_present_obj(*state, verbose ? "DEFAULT %{default}s" : "DEFAULT");
+
+ /* mark the end of column object */
+ pushJsonbValue(state, WJB_END_OBJECT, NULL);
+
+ /* Generated columns are not supported on typed tables, so we are done */
+
+ ReleaseSysCache(attrTup);
+}
+
+
+/*
+ * Subroutine for CREATE TABLE deparsing.
+ *
+ * Deal with all the table elements (columns and constraints).
+ *
+ * Note we ignore constraints in the parse node here; they are extracted from
+ * system catalogs instead.
+ */
+
+static void
+deparse_TableElementsToJsonb(JsonbParseState **state, Relation relation,
+ List *tableElements, List *dpcontext,
+ bool typed, bool composite, bool *belemsAdded)
+{
+ ListCell *lc;
+
+ foreach(lc, tableElements)
+ {
+ Node *elt = (Node *) lfirst(lc);
+
+ switch (nodeTag(elt))
+ {
+ case T_ColumnDef:
+ {
+ if (typed)
+ deparse_ColumnDef_typed_toJsonb(state, relation, dpcontext,
+ (ColumnDef *) elt, belemsAdded);
+ else
+ deparse_ColumnDef_toJsonb(state, relation, dpcontext,
+ composite, (ColumnDef *) elt,
+ false, NULL, belemsAdded);
+ }
+ break;
+ case T_Constraint:
+ break;
+ default:
+ elog(ERROR, "invalid node type %d", nodeTag(elt));
+ }
+ }
+
+}
+
+/*
+ * Subroutine for CREATE TABLE deparsing.
+ *
+ * Deparse the INHERITS relations.
+ *
+ * Given a table OID, return a schema-qualified table list representing
+ * the parent tables.
+ */
+static List *
+deparse_InhRelationsToJsonb(Oid objectId)
+{
+ List *parents = NIL;
+ Relation inhRel;
+ SysScanDesc scan;
+ ScanKeyData key;
+ HeapTuple tuple;
+
+ inhRel = table_open(InheritsRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key,
+ Anum_pg_inherits_inhrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId,
+ true, NULL, 1, &key);
+
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+ {
+ Form_pg_inherits formInh = (Form_pg_inherits) GETSTRUCT(tuple);
+
+ parents = lappend_oid(parents, formInh->inhparent);
+ }
+
+ systable_endscan(scan);
+ table_close(inhRel, RowExclusiveLock);
+
+ return parents;
+}
+
+/*
+ * Subroutine for CREATE TABLE deparsing.
+ *
+ * Given a table OID, obtain its constraints and append them to the given
+ * JsonbParseState.
+ *
+ * This works for typed tables, regular tables.
+ *
+ * Note that CONSTRAINT_FOREIGN constraints are always ignored.
+ */
+static void
+obtainConstraintsInJsonb(JsonbParseState **state, Oid relationId,
+ bool *belemsAdded)
+{
+ Relation conRel;
+ ScanKeyData key;
+ SysScanDesc scan;
+ HeapTuple tuple;
+
+ Assert(OidIsValid(relationId));
+
+ /*
+ * Scan pg_constraint to fetch all constraints linked to the given
+ * relation.
+ */
+ conRel = table_open(ConstraintRelationId, AccessShareLock);
+ ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber,
+ F_OIDEQ, ObjectIdGetDatum(relationId));
+ scan = systable_beginscan(conRel, ConstraintRelidTypidNameIndexId, true,
+ NULL, 1, &key);
+
+ /*
+ * For each constraint, add a node to the list of table elements. In
+ * these nodes we include not only the printable information ("fmt"), but
+ * also separate attributes to indicate the type of constraint, for
+ * automatic processing.
+ */
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+ {
+ Form_pg_constraint constrForm;
+ char *contype;
+ char *fmtStr = "CONSTRAINT %{name}I %{definition}s";
+
+ constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
+
+ switch (constrForm->contype)
+ {
+ case CONSTRAINT_CHECK:
+ contype = "check";
+ break;
+ case CONSTRAINT_FOREIGN:
+ continue; /* not here */
+ case CONSTRAINT_PRIMARY:
+ contype = "primary key";
+ break;
+ case CONSTRAINT_UNIQUE:
+ contype = "unique";
+ break;
+ case CONSTRAINT_EXCLUSION:
+ contype = "exclusion";
+ break;
+ default:
+ elog(ERROR, "unrecognized constraint type");
+ }
+
+ if (!*belemsAdded)
+ {
+ *belemsAdded = true;
+ insert_jsonb_key(*state, "table_elements");
+ pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
+
+ fmt_to_jsonb_element(*state, "(%{elements:, }s)");
+
+ insert_jsonb_key(*state, "elements");
+ pushJsonbValue(state, WJB_BEGIN_ARRAY, NULL);
+ }
+
+ /*
+ * "type" and "contype" are not part of the printable output, but are
+ * useful to programmatically distinguish these from columns and among
+ * different constraint types.
+ *
+ * XXX it might be useful to also list the column names in a PK, etc.
+ */
+
+ pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(*state, fmtStr);
+
+ /* create name and type elements for constraint */
+ insert_jsonb_str_element(*state, "type", "constraint");
+ insert_jsonb_str_element(*state, "contype", contype);
+ insert_jsonb_str_element(*state, "name", NameStr(constrForm->conname));
+ insert_jsonb_str_element(*state, "definition",
+ pg_get_constraintdef_string(constrForm->oid));
+
+ if (constrForm->conindid &&
+ (constrForm->contype == CONSTRAINT_PRIMARY ||
+ constrForm->contype == CONSTRAINT_UNIQUE ||
+ constrForm->contype == CONSTRAINT_EXCLUSION))
+ {
+ Oid tblspc = get_rel_tablespace(constrForm->conindid);
+
+ if (OidIsValid(tblspc))
+ {
+ char *tblspcname = get_tablespace_name(tblspc);
+
+ if (!tblspcname)
+ elog(ERROR, "cache lookup failed for tablespace %u", tblspc);
+
+ fmt_to_jsonb_element(*state, "USING INDEX TABLESPACE %{tblspc}s");
+ insert_jsonb_str_element(*state, "tblspc", tblspcname);
+ }
+ }
+
+ pushJsonbValue(state, WJB_END_OBJECT, NULL);
+ }
+
+ systable_endscan(scan);
+ table_close(conRel, AccessShareLock);
+}
+
+/*
+ * Deparse DefElems, as used by Create Table
+ *
+ * Verbose syntax
+ * %{label}s = %{value}L
+ */
+static void
+deparse_DefElem_ToJsonb(JsonbParseState *state, DefElem *elem, bool is_reset)
+{
+ StringInfoData fmtStr;
+ initStringInfo(&fmtStr);
+
+ appendStringInfoString(&fmtStr, "%{label}s");
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+ /* LABEL */
+ {
+ StringInfoData labelfmt;
+ initStringInfo(&labelfmt);
+
+ /* insert LABEL as key */
+ insert_jsonb_key(state, "label");
+
+ /* LABEL's value is an object */
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+ if (elem->defnamespace != NULL)
+ {
+ appendStringInfoString(&labelfmt, "%{schema}I.");
+ insert_jsonb_str_element(state, "schema", elem->defnamespace);
+ }
+
+ appendStringInfoString(&labelfmt, "%{label}I");
+ insert_jsonb_str_element(state, "label", elem->defname);
+
+ fmt_to_jsonb_element(state, labelfmt.data);
+ pfree(labelfmt.data);
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+ }
+
+ /* VALUE */
+ if (!is_reset)
+ {
+ appendStringInfoString(&fmtStr, "= %{value}L");
+ insert_jsonb_str_element(state, "value",
+ elem->arg ? defGetString(elem) :
+ defGetBoolean(elem) ? "TRUE" : "FALSE");
+ }
+
+ fmt_to_jsonb_element(state, fmtStr.data);
+ pfree(fmtStr.data);
+
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Subroutine for CREATE TABLE deparsing.
+ *
+ * Insert with-clause object for table definition
+ */
+static void
+deparse_with_object(JsonbParseState *state, char *fmt, CreateStmt *node)
+{
+ ListCell *cell;
+
+ /* with_clause's value is an object */
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+ fmt_to_jsonb_element(state, fmt);
+
+ /* WITH */
+ {
+ /* insert with as key */
+ insert_jsonb_key(state, "with");
+
+ /* with's value is an array */
+ pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
+ /* add elements to array */
+ foreach(cell, node->options)
+ {
+ DefElem *opt = (DefElem *) lfirst(cell);
+ deparse_DefElem_ToJsonb(state, opt, false);
+ }
+
+ /* with's array end */
+ pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+ }
+
+ /* with_clause's object end */
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+
+/*
+ * Deparse a CreateStmt (CREATE TABLE).
+ *
+ * Given a table OID and the parse tree that created it, return JsonbValue
+ * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D [OF
+ * %{of_type}T | PARTITION OF %{parent_identity}D] %{table_elements}s
+ * %{inherits}s %{partition_by}s %{access_method}s %{with_clause}s
+ * %{on_commit}s %{tablespace}s
+ */
+static JsonbValue *
+parsetree_to_jsonb_create_table(Oid objectId, Node *parsetree, char *owner)
+{
+ CreateStmt *node = (CreateStmt *) parsetree;
+ Relation relation = relation_open(objectId, AccessShareLock);
+ Oid nspid = relation->rd_rel->relnamespace;
+ char *relname = RelationGetRelationName(relation);
+ List *dpcontext;
+ bool belemsAdded = false;
+ StringInfoData fmtStr;
+ JsonbParseState *state = NULL;
+
+ initStringInfo(&fmtStr);
+
+ dpcontext = deparse_context_for(RelationGetRelationName(relation),
+ objectId);
+
+ appendStringInfoString(&fmtStr, "CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D");
+
+ /* mark the start of ROOT object */
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+ /* create owner jsonb element */
+ role_to_jsonb_element(state, owner);
+
+ /* PERSISTENCE clause creation */
+ insert_jsonb_str_element(state, "persistence", get_persistence_str(relation->rd_rel->relpersistence));
+
+ /* IF NOT EXISTS clause creation */
+ insert_jsonb_str_element(state, "if_not_exists", node->if_not_exists ? "IF NOT EXISTS" : "");
+
+
+ /* IDENTITY creation */
+ insert_identity_object(state, nspid, relname);
+
+ /*
+ * TABLE-ELEMENTS array creation
+ *
+ * Typed tables and partitions use a slightly different format string: we
+ * must not put table_elements with parents directly in the fmt string,
+ * 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)
+ {
+ /*
+ * 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 use an indirection element and set
+ * present=false when there are no elements.
+ */
+ if (node->ofTypename)
+ {
+ appendStringInfoString(&fmtStr, " OF %{of_type}T");
+
+ /* Push the key first */
+ insert_jsonb_key(state, "of_type");
+
+ /* Push the value */
+ new_jsonb_for_type(state, relation->rd_rel->reloftype, -1);
+ }
+ else
+ {
+ List *parents;
+ Oid objid;
+
+ appendStringInfoString(&fmtStr, " PARTITION OF %{parent_identity}D");
+
+ parents = deparse_InhRelationsToJsonb(objectId);
+ objid = linitial_oid(parents);
+
+ Assert(list_length(parents) == 1);
+
+ new_jsonb_for_qualname_id(state, RelationRelationId,
+ objid, "parent_identity");
+ }
+
+ deparse_TableElementsToJsonb(&state, relation, node->tableElts, dpcontext,
+ true, /* typed table */
+ false, /* not composite */
+ &belemsAdded);
+
+ obtainConstraintsInJsonb(&state, objectId, &belemsAdded);
+
+ appendStringInfoString(&fmtStr, " %{table_elements}s");
+
+ if (belemsAdded)
+ {
+ pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+ }
+ else
+ {
+ insert_jsonb_key(state, "table_elements");
+ insert_not_present_obj(state, "(%{elements:, }s)");
+ }
+ }
+ else
+ {
+ List *inhrelations;
+ belemsAdded = true;
+
+ /*
+ * There is no need to process LIKE clauses separately; they have
+ * already been transformed into columns and constraints.
+ */
+
+ /*
+ * Process table elements: column definitions and constraints. Only
+ * the column definitions are obtained from the parse node itself. To
+ * get constraints we rely on pg_constraint, because the parse node
+ * might be missing some things such as the name of the constraints.
+ */
+ appendStringInfoString(&fmtStr, " (%{table_elements:, }s)");
+
+ insert_jsonb_key(state, "table_elements");
+
+ /*
+ * It will be of array type for multi-columns table, so lets begin an
+ * arrayobject. deparse_TableElementsToJsonb() will add elements to
+ * it.
+ */
+ pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
+ deparse_TableElementsToJsonb(&state, relation, node->tableElts, dpcontext,
+ false, /* not typed table */
+ false, /* not composite */
+ &belemsAdded);
+ obtainConstraintsInJsonb(&state, objectId, &belemsAdded);
+
+ pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+ appendStringInfoString(&fmtStr, " %{inherits}s");
+ insert_jsonb_key(state, "inherits");
+
+ /*
+ * 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)
+ {
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+ fmt_to_jsonb_element(state, "INHERITS (%{parents:, }D)");
+ inhrelations = deparse_InhRelationsToJsonb(objectId);
+
+ insert_jsonb_array_oid(state, "parents", inhrelations);
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+ }
+ else
+ insert_not_present_obj(state, verbose ? "INHERITS (%{parents:, }D)" : "INHERITS");
+ }
+
+
+ /* FOR VALUES clause */
+ if (node->partbound)
+ {
+ appendStringInfoString(&fmtStr, " %{partition_bound}s");
+ insert_jsonb_key(state, "partition_bound");
+
+ /*
+ * Get pg_class.relpartbound. We cannot use partbound in the parsetree
+ * directly as it's the original partbound expression which haven't
+ * been transformed.
+ */
+ insert_jsonb_str_element(state, "partition_bound",
+ RelationGetPartitionBound(objectId));
+ }
+
+ /* PARTITION BY clause */
+ appendStringInfoString(&fmtStr, " %{partition_by}s");
+ insert_jsonb_key(state, "partition_by");
+
+ if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ insert_str_object(state, "PARTITION BY %{definition}s", "definition",
+ pg_get_partkeydef_string(objectId));
+ else
+ insert_not_present_obj(state, verbose ? "PARTITION BY %{definition}s" : "PARTITION BY");
+
+
+ /* USING clause */
+ appendStringInfoString(&fmtStr, " %{access_method}s");
+ insert_jsonb_key(state, "access_method");
+
+ if (node->accessMethod)
+ insert_str_object(state, "USING %{access_method}I", "access_method",
+ node->accessMethod);
+ else
+ insert_not_present_obj(state, verbose ? "USING %{access_method}I" : "USING");
+
+ /* WITH clause */
+ appendStringInfoString(&fmtStr, " %{with_clause}s");
+ insert_jsonb_key(state, "with_clause");
+
+ if (node->options)
+ deparse_with_object(state, "WITH (%{with:, }s)", node);
+ else
+ insert_not_present_obj(state, verbose ? "WITH (%{with:, }s)" : "WITH");
+
+ /* TABLESPACE */
+ appendStringInfoString(&fmtStr, " %{tablespace}s");
+ insert_jsonb_key(state, "tablespace");
+
+ /* Push the value now: object in this case */
+ if (node->tablespacename)
+ insert_str_object(state, "TABLESPACE %{tablespace}I", "tablespace", node->tablespacename);
+ else
+ insert_not_present_obj(state, verbose ? "TABLESPACE %{tablespace}I" : "TABLESPACE");
+
+
+ relation_close(relation, AccessShareLock);
+
+ /* We have full fmt by now, so add jsonb element for that */
+ fmt_to_jsonb_element(state, fmtStr.data);
+
+ pfree(fmtStr.data);
+
+ /* Mark the end of ROOT object */
+ return pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
--
2.34.1