On Tue, May 12, 2015 at 12:25 AM, Alvaro Herrera <alvhe...@2ndquadrant.com> wrote:
> David Steele wrote: > > > I have reviewed and tested this patch and everything looks good to me. > > It also looks like you added better coverage for schema DDL, which is a > > welcome addition. > > Thanks -- I have pushed this now. > Hi, I've tried compiling the 0003-ddl_deparse-extension part from http://www.postgresql.org/message-id/20150409161419.gc4...@alvh.no-ip.org on current master and that has failed because the 0002 part hasn't been actually pushed (I've asked Alvaro off the list about this, that's how I know the reason ;-). I was able to update the 0002 part so it applies cleanly (updated version attached), and then the contrib module compiles after one minor change and seems to work. I've started to look into what it would take to move 0002's code to the extension itself, and I've got a question about use of printTypmod() in format_type_detailed(): if (typemod >= 0) *typemodstr = printTypmod(NULL, typemod, typeform->typmodout); else *typemodstr = pstrdup(""); Given that printTypmod() does psprintf("%s%s") one way or the other, shouldn't we pass an empty string here instead of NULL as typname argument? My hope is to get this test module extended quite a bit, not only to > cover existing commands, but also so that it causes future changes to > cause failure unless command collection is considered. (In a previous > version of this patch, there was a test mode that ran everything in the > serial_schedule of regular regression tests. That was IMV a good way to > ensure that new commands were also tested to run under command > collection. That hasn't been enabled in the new test module, and I > think it's necessary.) > > If anyone wants to contribute to the test module so that more is > covered, that would be much appreciated. > I'm planning to have a look at this part also. -- Alex
From 5381f5efafd1c2fd29b7c842e5ef1edd552d545e Mon Sep 17 00:00:00 2001 From: Oleksandr Shulgin <oleksandr.shul...@zalando.de> Date: Wed, 29 Jul 2015 11:09:56 +0200 Subject: [PATCH] ddl_deparse core support --- src/backend/commands/seclabel.c | 5 + src/backend/commands/sequence.c | 34 +++ src/backend/commands/tablecmds.c | 3 +- src/backend/utils/adt/format_type.c | 127 +++++++++ src/backend/utils/adt/regproc.c | 101 +++++-- src/backend/utils/adt/ruleutils.c | 516 ++++++++++++++++++++++++++++++++---- src/include/commands/sequence.h | 1 + src/include/utils/builtins.h | 4 + src/include/utils/ruleutils.h | 21 +- 9 files changed, 730 insertions(+), 82 deletions(-) diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c index 1ef98ce..aa4de97 100644 --- a/src/backend/commands/seclabel.c +++ b/src/backend/commands/seclabel.c @@ -63,6 +63,11 @@ ExecSecLabelStmt(SecLabelStmt *stmt) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("must specify provider when multiple security label providers have been loaded"))); provider = (LabelProvider *) linitial(label_provider_list); + /* + * Set the provider in the statement so that DDL deparse can use + * provider explicitly in generated statement. + */ + stmt->provider = (char *) provider->provider_name; } else { diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 9c1037f..b0f8003 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1517,6 +1517,40 @@ process_owned_by(Relation seqrel, List *owned_by) relation_close(tablerel, NoLock); } +/* + * Return sequence parameters, detailed + */ +Form_pg_sequence +get_sequence_values(Oid sequenceId) +{ + Buffer buf; + SeqTable elm; + Relation seqrel; + HeapTupleData seqtuple; + Form_pg_sequence seq; + Form_pg_sequence retSeq; + + retSeq = palloc(sizeof(FormData_pg_sequence)); + + /* open and AccessShareLock sequence */ + init_sequence(sequenceId, &elm, &seqrel); + + if (pg_class_aclcheck(sequenceId, GetUserId(), + ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + + seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple); + + memcpy(retSeq, seq, sizeof(FormData_pg_sequence)); + + UnlockReleaseBuffer(buf); + relation_close(seqrel, NoLock); + + return retSeq; +} /* * Return sequence parameters, for use by information schema diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index d394713..5058f9f 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8169,7 +8169,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, if (!list_member_oid(tab->changedConstraintOids, foundObject.objectId)) { - char *defstring = pg_get_constraintdef_string(foundObject.objectId); + char *defstring = pg_get_constraintdef_string(foundObject.objectId, + true); /* * Put NORMAL dependencies at the front of the list and diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index a851983..a8aacfb 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -328,6 +328,133 @@ format_type_internal(Oid type_oid, int32 typemod, /* + * Similar to format_type_internal, except we return each bit of information + * separately: + * + * - nspid is the schema OID. For certain SQL-standard types which have weird + * typmod rules, we return InvalidOid; caller is expected to not schema- + * qualify the name nor add quotes to the type name in this case. + * + * - typename is set to the type name, without quotes + * + * - typmod is set to the typemod, if any, as a string with parens + * + * - typarray indicates whether []s must be added + * + * We don't try to decode type names to their standard-mandated names, except + * in the cases of types with unusual typmod rules. + */ +void +format_type_detailed(Oid type_oid, int32 typemod, + Oid *nspid, char **typname, char **typemodstr, + bool *typarray) +{ + HeapTuple tuple; + Form_pg_type typeform; + Oid array_base_type; + + tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for type %u", type_oid); + + typeform = (Form_pg_type) GETSTRUCT(tuple); + + /* + * Special-case crock for types with strange typmod rules. + */ + if (type_oid == INTERVALOID || + type_oid == TIMESTAMPOID || + type_oid == TIMESTAMPTZOID || + type_oid == TIMEOID || + type_oid == TIMETZOID) + { + *typarray = false; + +peculiar_typmod: + switch (type_oid) + { + case INTERVALOID: + *typname = pstrdup("INTERVAL"); + break; + case TIMESTAMPTZOID: + if (typemod < 0) + { + *typname = pstrdup("TIMESTAMP WITH TIME ZONE"); + break; + } + /* otherwise, WITH TZ is added by typmod, so fall through */ + case TIMESTAMPOID: + *typname = pstrdup("TIMESTAMP"); + break; + case TIMETZOID: + if (typemod < 0) + { + *typname = pstrdup("TIME WITH TIME ZONE"); + break; + } + /* otherwise, WITH TZ is added by typmode, so fall through */ + case TIMEOID: + *typname = pstrdup("TIME"); + break; + } + *nspid = InvalidOid; + + if (typemod >= 0) + *typemodstr = printTypmod(NULL, typemod, typeform->typmodout); + else + *typemodstr = pstrdup(""); + + ReleaseSysCache(tuple); + return; + } + + /* + * Check if it's a regular (variable length) array type. As above, + * fixed-length array types such as "name" shouldn't get deconstructed. + */ + array_base_type = typeform->typelem; + + if (array_base_type != InvalidOid && + typeform->typstorage != 'p') + { + /* Switch our attention to the array element type */ + ReleaseSysCache(tuple); + tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for type %u", type_oid); + + typeform = (Form_pg_type) GETSTRUCT(tuple); + type_oid = array_base_type; + *typarray = true; + + /* + * If it's an array of one of the types with special typmod rules, + * have the element type be processed as above, but now with typarray + * set to true. + */ + if (type_oid == INTERVALOID || + type_oid == TIMESTAMPTZOID || + type_oid == TIMESTAMPOID || + type_oid == TIMETZOID || + type_oid == TIMEOID) + goto peculiar_typmod; + } + else + *typarray = false; + + *nspid = typeform->typnamespace; + *typname = pstrdup(NameStr(typeform->typname)); + + if (typemod >= 0) + *typemodstr = printTypmod(NULL, typemod, typeform->typmodout); + else + *typemodstr = pstrdup(""); + + ReleaseSysCache(tuple); +} + + +/* * Add typmod decoration to the basic type name */ static char * diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index 0bfeb5e..5efa5ea 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -43,7 +43,11 @@ #include "utils/acl.h" static char *format_operator_internal(Oid operator_oid, bool force_qualify); -static char *format_procedure_internal(Oid procedure_oid, bool force_qualify); +static char *format_procedure_internal(Oid procedure_oid, bool force_qualify, + bool args_only); +static void format_procedure_args_internal(Form_pg_proc procform, + StringInfo buf, bool force_qualify); + static void parseNameAndArgTypes(const char *string, bool allowNone, List **names, int *nargs, Oid *argtypes); @@ -364,13 +368,36 @@ to_regprocedure(PG_FUNCTION_ARGS) char * format_procedure(Oid procedure_oid) { - return format_procedure_internal(procedure_oid, false); + return format_procedure_internal(procedure_oid, false, false); } char * format_procedure_qualified(Oid procedure_oid) { - return format_procedure_internal(procedure_oid, true); + return format_procedure_internal(procedure_oid, true, false); +} + +/* + * format_procedure_args - converts proc OID to "(args)" + */ +char * +format_procedure_args(Oid procedure_oid, bool force_qualify) +{ + StringInfoData buf; + HeapTuple proctup; + Form_pg_proc procform; + + proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid)); + if (!HeapTupleIsValid(proctup)) + elog(ERROR, "cache lookup failed for procedure %u", procedure_oid); + procform = (Form_pg_proc) GETSTRUCT(proctup); + + initStringInfo(&buf); + format_procedure_args_internal(procform, &buf, force_qualify); + + ReleaseSysCache(proctup); + + return buf.data; } /* @@ -381,7 +408,7 @@ format_procedure_qualified(Oid procedure_oid) * qualified if the function is not in path. */ static char * -format_procedure_internal(Oid procedure_oid, bool force_qualify) +format_procedure_internal(Oid procedure_oid, bool force_qualify, bool args_only) { char *result; HeapTuple proctup; @@ -392,8 +419,6 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify) { Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup); char *proname = NameStr(procform->proname); - int nargs = procform->pronargs; - int i; char *nspname; StringInfoData buf; @@ -401,29 +426,24 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify) initStringInfo(&buf); - /* - * Would this proc be found (given the right args) by regprocedurein? - * If not, or if caller requests it, we need to qualify it. - */ - if (!force_qualify && FunctionIsVisible(procedure_oid)) - nspname = NULL; - else - nspname = get_namespace_name(procform->pronamespace); - - appendStringInfo(&buf, "%s(", - quote_qualified_identifier(nspname, proname)); - for (i = 0; i < nargs; i++) + if (!args_only) { - Oid thisargtype = procform->proargtypes.values[i]; - - if (i > 0) - appendStringInfoChar(&buf, ','); - appendStringInfoString(&buf, - force_qualify ? - format_type_be_qualified(thisargtype) : - format_type_be(thisargtype)); + /* + * Would this proc be found (given the right args) by + * regprocedurein? If not, or if caller requests it, we need to + * qualify it. + */ + if (!force_qualify && FunctionIsVisible(procedure_oid)) + nspname = NULL; + else + nspname = get_namespace_name(procform->pronamespace); + + appendStringInfo(&buf, "%s", + quote_qualified_identifier(nspname, proname)); } - appendStringInfoChar(&buf, ')'); + + /* add the attributes */ + format_procedure_args_internal(procform, &buf, force_qualify); result = buf.data; @@ -440,6 +460,33 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify) } /* + * Append the parenthised arguments of the given pg_proc row into the output + * buffer. force_qualify indicates whether to schema-qualify type names + * regardless of visibility. + */ +static void +format_procedure_args_internal(Form_pg_proc procform, StringInfo buf, + bool force_qualify) +{ + int i; + int nargs = procform->pronargs; + + appendStringInfoChar(buf, '('); + for (i = 0; i < nargs; i++) + { + Oid thisargtype = procform->proargtypes.values[i]; + + if (i > 0) + appendStringInfoChar(buf, ','); + appendStringInfoString(buf, + force_qualify ? + format_type_be_qualified(thisargtype) : + format_type_be(thisargtype)); + } + appendStringInfoChar(buf, ')'); +} + +/* * Output an objname/objargs representation for the procedure with the * given OID. If it doesn't exist, an error is thrown. * diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 5112cac..5cfaf9b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -462,6 +462,77 @@ pg_get_ruledef_ext(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(string_to_text(pg_get_ruledef_worker(ruleoid, prettyFlags))); } +/* + * Given a pair of Datum corresponding to a rule's pg_rewrite.ev_qual and + * ev_action columns, return their text representation; ev_qual as a single + * string in whereClause and ev_action as a List of strings (which might be + * NIL, signalling NOTHING) in actions. + */ +void +pg_get_ruledef_details(Datum ev_qual, Datum ev_action, + char **whereClause, List **actions) +{ + int prettyFlags = 0; + char *qualstr = TextDatumGetCString(ev_qual); + char *actionstr = TextDatumGetCString(ev_action); + List *actionNodeList = (List *) stringToNode(actionstr); + StringInfoData buf; + + initStringInfo(&buf); + if (strlen(qualstr) > 0 && strcmp(qualstr, "<>") != 0) + { + Node *qual; + Query *query; + deparse_context context; + deparse_namespace dpns; + + qual = stringToNode(qualstr); + + query = (Query *) linitial(actionNodeList); + query = getInsertSelectQuery(query, NULL); + + AcquireRewriteLocks(query, false, false); + + context.buf = &buf; + context.namespaces = list_make1(&dpns); + context.windowClause = NIL; + context.windowTList = NIL; + context.varprefix = (list_length(query->rtable) != 1); + context.prettyFlags = prettyFlags; + context.wrapColumn = WRAP_COLUMN_DEFAULT; + context.indentLevel = PRETTYINDENT_STD; + + set_deparse_for_query(&dpns, query, NIL); + + get_rule_expr(qual, &context, false); + + *whereClause = pstrdup(buf.data); + } + else + *whereClause = NULL; + + if (list_length(actionNodeList) == 0) + *actions = NIL; + else + { + ListCell *cell; + List *output = NIL; + + foreach(cell, actionNodeList) + { + Query *query = (Query *) lfirst(cell); + + if (query->commandType == CMD_NOTHING) + continue; + + resetStringInfo(&buf); + get_query_def(query, &buf, NIL, NULL, + prettyFlags, WRAP_COLUMN_DEFAULT, 0); + output = lappend(output, pstrdup(buf.data)); + } + *actions = output; + } +} static char * pg_get_ruledef_worker(Oid ruleoid, int prettyFlags) @@ -612,6 +683,13 @@ pg_get_viewdef_name_ext(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT))); } +char * +pg_get_viewdef_internal(Oid viewoid) +{ + return pg_get_viewdef_worker(viewoid, 0, WRAP_COLUMN_DEFAULT); +} + + /* * Common code for by-OID and by-name variants of pg_get_viewdef */ @@ -686,6 +764,21 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn) return buf.data; } +/* + * get_createtableas_def - Get the accompanying query for a CREATE TABLE AS + */ +char * +pg_get_createtableas_def(Query *query) +{ + StringInfoData buf; + + initStringInfo(&buf); + + get_query_def(query, &buf, NIL, NULL, 0, 0, 0); + + return buf.data; +} + /* ---------- * get_triggerdef - Get the definition of a trigger * ---------- @@ -836,60 +929,12 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty) if (!isnull) { Node *qual; - char relkind; - deparse_context context; - deparse_namespace dpns; - RangeTblEntry *oldrte; - RangeTblEntry *newrte; - - appendStringInfoString(&buf, "WHEN ("); + char *qualstr; qual = stringToNode(TextDatumGetCString(value)); + qualstr = pg_get_trigger_whenclause(trigrec, qual, pretty); - relkind = get_rel_relkind(trigrec->tgrelid); - - /* Build minimal OLD and NEW RTEs for the rel */ - oldrte = makeNode(RangeTblEntry); - oldrte->rtekind = RTE_RELATION; - oldrte->relid = trigrec->tgrelid; - oldrte->relkind = relkind; - oldrte->alias = makeAlias("old", NIL); - oldrte->eref = oldrte->alias; - oldrte->lateral = false; - oldrte->inh = false; - oldrte->inFromCl = true; - - newrte = makeNode(RangeTblEntry); - newrte->rtekind = RTE_RELATION; - newrte->relid = trigrec->tgrelid; - newrte->relkind = relkind; - newrte->alias = makeAlias("new", NIL); - newrte->eref = newrte->alias; - newrte->lateral = false; - newrte->inh = false; - newrte->inFromCl = true; - - /* Build two-element rtable */ - memset(&dpns, 0, sizeof(dpns)); - dpns.rtable = list_make2(oldrte, newrte); - dpns.ctes = NIL; - set_rtable_names(&dpns, NIL, NULL); - set_simple_column_names(&dpns); - - /* Set up context with one-deep namespace stack */ - context.buf = &buf; - context.namespaces = list_make1(&dpns); - context.windowClause = NIL; - context.windowTList = NIL; - context.varprefix = true; - context.prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT; - context.wrapColumn = WRAP_COLUMN_DEFAULT; - context.indentLevel = PRETTYINDENT_STD; - context.special_exprkind = EXPR_KIND_NONE; - - get_rule_expr(qual, &context, false); - - appendStringInfoString(&buf, ") "); + appendStringInfo(&buf, "WHEN (%s) ", qualstr); } appendStringInfo(&buf, "EXECUTE PROCEDURE %s(", @@ -930,6 +975,64 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty) return buf.data; } +char * +pg_get_trigger_whenclause(Form_pg_trigger trigrec, Node *whenClause, bool pretty) +{ + StringInfoData buf; + char relkind; + deparse_context context; + deparse_namespace dpns; + RangeTblEntry *oldrte; + RangeTblEntry *newrte; + + initStringInfo(&buf); + + relkind = get_rel_relkind(trigrec->tgrelid); + + /* Build minimal OLD and NEW RTEs for the rel */ + oldrte = makeNode(RangeTblEntry); + oldrte->rtekind = RTE_RELATION; + oldrte->relid = trigrec->tgrelid; + oldrte->relkind = relkind; + oldrte->alias = makeAlias("old", NIL); + oldrte->eref = oldrte->alias; + oldrte->lateral = false; + oldrte->inh = false; + oldrte->inFromCl = true; + + newrte = makeNode(RangeTblEntry); + newrte->rtekind = RTE_RELATION; + newrte->relid = trigrec->tgrelid; + newrte->relkind = relkind; + newrte->alias = makeAlias("new", NIL); + newrte->eref = newrte->alias; + newrte->lateral = false; + newrte->inh = false; + newrte->inFromCl = true; + + /* Build two-element rtable */ + memset(&dpns, 0, sizeof(dpns)); + dpns.rtable = list_make2(oldrte, newrte); + dpns.ctes = NIL; + set_rtable_names(&dpns, NIL, NULL); + set_simple_column_names(&dpns); + + /* Set up context with one-deep namespace stack */ + context.buf = &buf; + context.namespaces = list_make1(&dpns); + context.windowClause = NIL; + context.windowTList = NIL; + context.varprefix = true; + context.prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT; + context.wrapColumn = WRAP_COLUMN_DEFAULT; + context.indentLevel = PRETTYINDENT_STD; + context.special_exprkind = EXPR_KIND_NONE; + + get_rule_expr(whenClause, &context, false); + + return buf.data; +} + /* ---------- * get_indexdef - Get the definition of an index * @@ -993,6 +1096,8 @@ pg_get_indexdef_columns(Oid indexrelid, bool pretty) * * This is now used for exclusion constraints as well: if excludeOps is not * NULL then it points to an array of exclusion operator OIDs. + * + * XXX if you change this function, see pg_get_indexdef_detailed too. */ static char * pg_get_indexdef_worker(Oid indexrelid, int colno, @@ -1272,6 +1377,245 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * Return an index definition, split in several pieces. + * + * There is a huge lot of code that's a dupe of pg_get_indexdef_worker, but + * control flow is different enough that it doesn't seem worth keeping them + * together. + */ +void +pg_get_indexdef_detailed(Oid indexrelid, + char **index_am, + char **definition, + char **reloptions, + char **tablespace, + char **whereClause) +{ + HeapTuple ht_idx; + HeapTuple ht_idxrel; + HeapTuple ht_am; + Form_pg_index idxrec; + Form_pg_class idxrelrec; + Form_pg_am amrec; + List *indexprs; + ListCell *indexpr_item; + List *context; + Oid indrelid; + int keyno; + Datum indcollDatum; + Datum indclassDatum; + Datum indoptionDatum; + bool isnull; + oidvector *indcollation; + oidvector *indclass; + int2vector *indoption; + StringInfoData definitionBuf; + char *sep; + + /* + * Fetch the pg_index tuple by the Oid of the index + */ + ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid)); + if (!HeapTupleIsValid(ht_idx)) + elog(ERROR, "cache lookup failed for index %u", indexrelid); + idxrec = (Form_pg_index) GETSTRUCT(ht_idx); + + indrelid = idxrec->indrelid; + Assert(indexrelid == idxrec->indexrelid); + + /* Must get indcollation, indclass, and indoption the hard way */ + indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx, + Anum_pg_index_indcollation, &isnull); + Assert(!isnull); + indcollation = (oidvector *) DatumGetPointer(indcollDatum); + + indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx, + Anum_pg_index_indclass, &isnull); + Assert(!isnull); + indclass = (oidvector *) DatumGetPointer(indclassDatum); + + indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx, + Anum_pg_index_indoption, &isnull); + Assert(!isnull); + indoption = (int2vector *) DatumGetPointer(indoptionDatum); + + /* + * Fetch the pg_class tuple of the index relation + */ + ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid)); + if (!HeapTupleIsValid(ht_idxrel)) + elog(ERROR, "cache lookup failed for relation %u", indexrelid); + idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel); + + /* + * Fetch the pg_am tuple of the index' access method + */ + ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam)); + if (!HeapTupleIsValid(ht_am)) + elog(ERROR, "cache lookup failed for access method %u", + idxrelrec->relam); + amrec = (Form_pg_am) GETSTRUCT(ht_am); + + /* + * Get the index expressions, if any. (NOTE: we do not use the relcache + * versions of the expressions and predicate, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx, + Anum_pg_index_indexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + indexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + indexprs = NIL; + + indexpr_item = list_head(indexprs); + + context = deparse_context_for(get_relation_name(indrelid), indrelid); + + initStringInfo(&definitionBuf); + + /* output index AM */ + *index_am = pstrdup(quote_identifier(NameStr(amrec->amname))); + + /* + * Output index definition. Note the outer parens must be supplied by + * caller. + */ + sep = ""; + for (keyno = 0; keyno < idxrec->indnatts; keyno++) + { + AttrNumber attnum = idxrec->indkey.values[keyno]; + int16 opt = indoption->values[keyno]; + Oid keycoltype; + Oid keycolcollation; + Oid indcoll; + + appendStringInfoString(&definitionBuf, sep); + sep = ", "; + + if (attnum != 0) + { + /* Simple index column */ + char *attname; + int32 keycoltypmod; + + attname = get_relid_attribute_name(indrelid, attnum); + appendStringInfoString(&definitionBuf, quote_identifier(attname)); + get_atttypetypmodcoll(indrelid, attnum, + &keycoltype, &keycoltypmod, + &keycolcollation); + } + else + { + /* expressional index */ + Node *indexkey; + char *str; + + if (indexpr_item == NULL) + elog(ERROR, "too few entries in indexprs list"); + indexkey = (Node *) lfirst(indexpr_item); + indexpr_item = lnext(indexpr_item); + /* Deparse */ + str = deparse_expression_pretty(indexkey, context, false, false, + 0, 0); + + /* Need parens if it's not a bare function call */ + if (indexkey && IsA(indexkey, FuncExpr) && + ((FuncExpr *) indexkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&definitionBuf, str); + else + appendStringInfo(&definitionBuf, "(%s)", str); + + keycoltype = exprType(indexkey); + keycolcollation = exprCollation(indexkey); + } + + /* Add collation, even if default */ + indcoll = indcollation->values[keyno]; + if (OidIsValid(indcoll)) + appendStringInfo(&definitionBuf, " COLLATE %s", + generate_collation_name((indcoll))); + + /* Add the operator class name, even if default */ + get_opclass_name(indclass->values[keyno], InvalidOid, &definitionBuf); + + /* Add options if relevant */ + if (amrec->amcanorder) + { + /* if it supports sort ordering, report DESC and NULLS opts */ + if (opt & INDOPTION_DESC) + { + appendStringInfoString(&definitionBuf, " DESC"); + /* NULLS FIRST is the default in this case */ + if (!(opt & INDOPTION_NULLS_FIRST)) + appendStringInfoString(&definitionBuf, " NULLS LAST"); + } + else + { + if (opt & INDOPTION_NULLS_FIRST) + appendStringInfoString(&definitionBuf, " NULLS FIRST"); + } + } + + /* XXX excludeOps thingy was here; do we need anything? */ + } + *definition = definitionBuf.data; + + /* output reloptions */ + *reloptions = flatten_reloptions(indexrelid); + + /* output tablespace */ + { + Oid tblspc; + + tblspc = get_rel_tablespace(indexrelid); + if (OidIsValid(tblspc)) + *tablespace = pstrdup(quote_identifier(get_tablespace_name(tblspc))); + else + *tablespace = NULL; + } + + /* report index predicate, if any */ + if (!heap_attisnull(ht_idx, Anum_pg_index_indpred)) + { + Node *node; + Datum predDatum; + bool isnull; + char *predString; + + /* Convert text string to node tree */ + predDatum = SysCacheGetAttr(INDEXRELID, ht_idx, + Anum_pg_index_indpred, &isnull); + Assert(!isnull); + predString = TextDatumGetCString(predDatum); + node = (Node *) stringToNode(predString); + pfree(predString); + + /* Deparse */ + *whereClause = + deparse_expression_pretty(node, context, false, false, + 0, 0); + } + else + *whereClause = NULL; + + /* Clean up */ + ReleaseSysCache(ht_idx); + ReleaseSysCache(ht_idxrel); + ReleaseSysCache(ht_am); + + /* all done */ +} /* * pg_get_constraintdef @@ -1306,9 +1650,9 @@ pg_get_constraintdef_ext(PG_FUNCTION_ARGS) /* Internal version that returns a palloc'd C string; no pretty-printing */ char * -pg_get_constraintdef_string(Oid constraintId) +pg_get_constraintdef_string(Oid constraintId, bool fullCommand) { - return pg_get_constraintdef_worker(constraintId, true, 0); + return pg_get_constraintdef_worker(constraintId, fullCommand, 0); } /* @@ -9753,3 +10097,69 @@ flatten_reloptions(Oid relid) return result; } + +/* + * Obtain the deparsed default value for the given column of the given table. + * + * Caller must have set a correct deparse context. + */ +char * +RelationGetColumnDefault(Relation rel, AttrNumber attno, List *dpcontext) +{ + Node *defval; + char *defstr; + + defval = build_column_default(rel, attno); + defstr = deparse_expression_pretty(defval, dpcontext, false, false, + 0, 0); + + return defstr; +} + +/* + * Return the default value of a domain. + */ +char * +DomainGetDefault(HeapTuple domTup) +{ + Datum def; + Node *defval; + char *defstr; + bool isnull; + + def = SysCacheGetAttr(TYPEOID, domTup, Anum_pg_type_typdefaultbin, + &isnull); + if (isnull) + elog(ERROR, "domain \"%s\" does not have a default value", + NameStr(((Form_pg_type) GETSTRUCT(domTup))->typname)); + defval = stringToNode(TextDatumGetCString(def)); + defstr = deparse_expression_pretty(defval, NULL /* dpcontext? */, + false, false, 0, 0); + + return defstr; +} + +/* + * Return the defaults values of arguments to a function, as a list of + * deparsed expressions. + */ +List * +FunctionGetDefaults(text *proargdefaults) +{ + List *nodedefs; + List *strdefs = NIL; + ListCell *cell; + + nodedefs = (List *) stringToNode(TextDatumGetCString(proargdefaults)); + if (!IsA(nodedefs, List)) + elog(ERROR, "proargdefaults is not a list"); + + foreach(cell, nodedefs) + { + Node *onedef = lfirst(cell); + + strdefs = lappend(strdefs, deparse_expression_pretty(onedef, NIL, false, false, 0, 0)); + } + + return strdefs; +} diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 44862bb..8d8e556 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -72,6 +72,7 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS); extern Datum lastval(PG_FUNCTION_ARGS); extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS); +extern Form_pg_sequence get_sequence_values(Oid sequenceId); extern ObjectAddress DefineSequence(CreateSeqStmt *stmt); extern ObjectAddress AlterSequence(AlterSeqStmt *stmt); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index fcb0bf0..642e03d 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -660,6 +660,7 @@ extern char *format_procedure(Oid procedure_oid); extern char *format_procedure_qualified(Oid procedure_oid); extern void format_procedure_parts(Oid operator_oid, List **objnames, List **objargs); +extern char *format_procedure_args(Oid procedure_oid, bool force_qualify); extern char *format_operator(Oid operator_oid); extern char *format_operator_qualified(Oid operator_oid); extern void format_operator_parts(Oid operator_oid, List **objnames, @@ -1105,6 +1106,9 @@ extern char *format_type_be_qualified(Oid type_oid); extern char *format_type_with_typemod(Oid type_oid, int32 typemod); extern Datum oidvectortypes(PG_FUNCTION_ARGS); extern int32 type_maximum_size(Oid type_oid, int32 typemod); +extern void format_type_detailed(Oid type_oid, int32 typemod, + Oid *nspid, char **typname, + char **typemodstr, bool *is_array); /* quote.c */ extern Datum quote_ident(PG_FUNCTION_ARGS); diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h index 3494b13..8ff6a0c 100644 --- a/src/include/utils/ruleutils.h +++ b/src/include/utils/ruleutils.h @@ -13,6 +13,7 @@ #ifndef RULEUTILS_H #define RULEUTILS_H +#include "catalog/pg_trigger.h" #include "nodes/nodes.h" #include "nodes/parsenodes.h" #include "nodes/pg_list.h" @@ -20,8 +21,20 @@ extern char *pg_get_indexdef_string(Oid indexrelid); extern char *pg_get_indexdef_columns(Oid indexrelid, bool pretty); +extern void pg_get_indexdef_detailed(Oid indexrelid, + char **index_am, + char **definition, + char **reloptions, + char **tablespace, + char **whereClause); +extern char *pg_get_trigger_whenclause(Form_pg_trigger trigrec, + Node *whenClause, bool pretty); +extern char *pg_get_constraintdef_string(Oid constraintId, bool fullCommand); +extern void pg_get_ruledef_details(Datum ev_qual, Datum ev_action, + char **whereClause, List **actions); +extern char *pg_get_viewdef_internal(Oid viewoid); +extern char *pg_get_createtableas_def(Query *query); -extern char *pg_get_constraintdef_string(Oid constraintId); extern char *deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit); extern List *deparse_context_for(const char *aliasname, Oid relid); @@ -31,5 +44,11 @@ extern List *set_deparse_context_planstate(List *dpcontext, extern List *select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used); extern char *generate_collation_name(Oid collid); +extern List *FunctionGetDefaults(text *proargdefaults); + +extern char *RelationGetColumnDefault(Relation rel, AttrNumber attno, + List *dpcontext); + +extern char *DomainGetDefault(HeapTuple domTup); #endif /* RULEUTILS_H */ -- 2.1.4
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers