Hi list, Often enough when developing PostgreSQL views and functions, I have pasted the CREATE OR REPLACE commands into the wrong window/shell and ran them there without realizing that I'm creating a function in the wrong database, instead of replacing. Currently psql does not provide any feedback of which action really occured.
Only after writing this patch I realized that I could instead raise a NOTICE, like current IF EXISTS/IF NOT EXISTS clauses do. Is that a better way to solve this? This patch returns command tag "CREATE X" or "REPLACE X" for LANGAUGE/VIEW/RULE/FUNCTION. This is done by passing completionTag to from ProcessUtility to more functions, and adding a 'bool *didUpdate' argument to some lower-level functions. I'm not sure if passing back the status in a bool* is considered good style, but this way all the functions look consistent. Regards, Marti
From b848a4129e31aa021059dab5a0a7ad139fe018b3 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp <ma...@juffo.org> Date: Sun, 28 Nov 2010 16:49:41 +0200 Subject: [PATCH] Return command tag 'REPLACE X' for CREATE OR REPLACE statements. This affects CREATE OR REPLACE LANGUAGE/VIEW/RULE/FUNCTION --- src/backend/catalog/pg_aggregate.c | 3 ++- src/backend/catalog/pg_proc.c | 6 +++++- src/backend/commands/functioncmds.c | 16 +++++++++++++--- src/backend/commands/proclang.c | 30 ++++++++++++++++++++++-------- src/backend/commands/view.c | 18 ++++++++++++++---- src/backend/rewrite/rewriteDefine.c | 25 ++++++++++++++++++++----- src/backend/tcop/utility.c | 8 ++++---- src/include/catalog/pg_proc_fn.h | 3 ++- src/include/commands/defrem.h | 2 +- src/include/commands/proclang.h | 2 +- src/include/commands/view.h | 2 +- src/include/rewrite/rewriteDefine.h | 5 +++-- 12 files changed, 88 insertions(+), 32 deletions(-) diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index eadf41f..31e91b4 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -229,7 +229,8 @@ AggregateCreate(const char *aggName, NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ - 0); /* prorows */ + 0, /* prorows */ + NULL); /* didReplace */ /* * Okay to create the pg_aggregate entry. diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index d464979..e456139 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -85,7 +85,8 @@ ProcedureCreate(const char *procedureName, List *parameterDefaults, Datum proconfig, float4 procost, - float4 prorows) + float4 prorows, + bool *didReplace) { Oid retval; int parameterCount; @@ -650,6 +651,9 @@ ProcedureCreate(const char *procedureName, AtEOXact_GUC(true, save_nestlevel); } + if(didReplace) + *didReplace = is_update; + return retval; } diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 2347cad..4e07c9d 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -770,7 +770,7 @@ interpret_AS_clause(Oid languageOid, const char *languageName, * Execute a CREATE FUNCTION utility statement. */ void -CreateFunction(CreateFunctionStmt *stmt, const char *queryString) +CreateFunction(CreateFunctionStmt *stmt, const char *queryString, char *completionTag) { char *probin_str; char *prosrc_str; @@ -791,7 +791,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) Oid requiredResultType; bool isWindowFunc, isStrict, - security; + security, + didReplace; char volatility; ArrayType *proconfig; float4 procost; @@ -958,7 +959,16 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) parameterDefaults, PointerGetDatum(proconfig), procost, - prorows); + prorows, + &didReplace); + + if (completionTag) + { + if (didReplace) + strcpy(completionTag, "REPLACE FUNCTION"); + else + strcpy(completionTag, "CREATE FUNCTION"); + } } diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 9d0d3b2..10d3cd9 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -51,7 +51,7 @@ typedef struct static void create_proc_lang(const char *languageName, bool replace, Oid languageOwner, Oid handlerOid, Oid inlineOid, - Oid valOid, bool trusted); + Oid valOid, bool trusted, bool *didReplace); static PLTemplate *find_language_template(const char *languageName); static void AlterLanguageOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId); @@ -62,7 +62,7 @@ static void AlterLanguageOwner_internal(HeapTuple tup, Relation rel, * --------------------------------------------------------------------- */ void -CreateProceduralLanguage(CreatePLangStmt *stmt) +CreateProceduralLanguage(CreatePLangStmt *stmt, char *completionTag) { char *languageName; PLTemplate *pltemplate; @@ -71,6 +71,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) valOid; Oid funcrettype; Oid funcargtypes[1]; + bool didReplace; /* * Translate the language name to lower case @@ -146,7 +147,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + NULL); } /* @@ -181,7 +183,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + NULL); } } else @@ -219,7 +222,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + NULL); } } else @@ -228,7 +232,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) /* ok, create it */ create_proc_lang(languageName, stmt->replace, GetUserId(), handlerOid, inlineOid, - valOid, pltemplate->tmpltrusted); + valOid, pltemplate->tmpltrusted, &didReplace); } else { @@ -303,7 +307,15 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) /* ok, create it */ create_proc_lang(languageName, stmt->replace, GetUserId(), handlerOid, inlineOid, - valOid, stmt->pltrusted); + valOid, stmt->pltrusted, &didReplace); + } + + if (completionTag) + { + if (didReplace) + strcpy(completionTag, "REPLACE LANGUAGE"); + else + strcpy(completionTag, "CREATE LANGUAGE"); } } @@ -313,7 +325,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) static void create_proc_lang(const char *languageName, bool replace, Oid languageOwner, Oid handlerOid, Oid inlineOid, - Oid valOid, bool trusted) + Oid valOid, bool trusted, bool *didReplace) { Relation rel; TupleDesc tupDesc; @@ -431,6 +443,8 @@ create_proc_lang(const char *languageName, bool replace, LanguageRelationId, myself.objectId, 0); heap_close(rel, RowExclusiveLock); + + *didReplace = is_update; } /* diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 09ab24b..ce92734 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -304,7 +304,7 @@ checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc) } static void -DefineViewRules(Oid viewOid, Query *viewParse, bool replace) +DefineViewRules(Oid viewOid, Query *viewParse, bool replace, bool *didReplace) { /* * Set up the ON SELECT rule. Since the query has already been through @@ -316,7 +316,8 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) CMD_SELECT, true, replace, - list_make1(viewParse)); + list_make1(viewParse), + didReplace); /* * Someday: automatic ON INSERT, etc @@ -394,11 +395,12 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) * Execute a CREATE VIEW command. */ void -DefineView(ViewStmt *stmt, const char *queryString) +DefineView(ViewStmt *stmt, const char *queryString, char *completionTag) { Query *viewParse; Oid viewOid; RangeVar *view; + bool didReplace; /* * Run parse analysis to convert the raw parse tree to a Query. Note this @@ -488,5 +490,13 @@ DefineView(ViewStmt *stmt, const char *queryString) /* * Now create the rules associated with the view. */ - DefineViewRules(viewOid, viewParse, stmt->replace); + DefineViewRules(viewOid, viewParse, stmt->replace, &didReplace); + + if (completionTag) + { + if (didReplace) + strcpy(completionTag, "REPLACE VIEW"); + else + strcpy(completionTag, "CREATE VIEW"); + } } diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 4354897..34d7963 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -56,7 +56,8 @@ InsertRule(char *rulname, bool evinstead, Node *event_qual, List *action, - bool replace) + bool replace, + bool *didReplace) { char *evqual = nodeToString(event_qual); char *actiontree = nodeToString((Node *) action); @@ -184,6 +185,8 @@ InsertRule(char *rulname, heap_close(pg_rewrite_desc, RowExclusiveLock); + *didReplace = is_update; + return rewriteObjectId; } @@ -192,11 +195,12 @@ InsertRule(char *rulname, * Execute a CREATE RULE command. */ void -DefineRule(RuleStmt *stmt, const char *queryString) +DefineRule(RuleStmt *stmt, const char *queryString, char *completionTag) { List *actions; Node *whereClause; Oid relId; + bool didReplace; /* Parse analysis ... */ transformRuleStmt(stmt, queryString, &actions, &whereClause); @@ -211,7 +215,16 @@ DefineRule(RuleStmt *stmt, const char *queryString) stmt->event, stmt->instead, stmt->replace, - actions); + actions, + &didReplace); + + if (completionTag) + { + if (didReplace) + strcpy(completionTag, "REPLACE RULE"); + else + strcpy(completionTag, "CREATE RULE"); + } } @@ -229,7 +242,8 @@ DefineQueryRewrite(char *rulename, CmdType event_type, bool is_instead, bool replace, - List *action) + List *action, + bool *didReplace) { Relation event_relation; Oid ruleId; @@ -487,7 +501,8 @@ DefineQueryRewrite(char *rulename, is_instead, event_qual, action, - replace); + replace, + didReplace); /* * Set pg_class 'relhasrules' field TRUE for event relation. If diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 803c603..2ab929f 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -872,11 +872,11 @@ standard_ProcessUtility(Node *parsetree, break; case T_ViewStmt: /* CREATE VIEW */ - DefineView((ViewStmt *) parsetree, queryString); + DefineView((ViewStmt *) parsetree, queryString, completionTag); break; case T_CreateFunctionStmt: /* CREATE FUNCTION */ - CreateFunction((CreateFunctionStmt *) parsetree, queryString); + CreateFunction((CreateFunctionStmt *) parsetree, queryString, completionTag); break; case T_AlterFunctionStmt: /* ALTER FUNCTION */ @@ -920,7 +920,7 @@ standard_ProcessUtility(Node *parsetree, break; case T_RuleStmt: /* CREATE RULE */ - DefineRule((RuleStmt *) parsetree, queryString); + DefineRule((RuleStmt *) parsetree, queryString, completionTag); break; case T_CreateSeqStmt: @@ -1091,7 +1091,7 @@ standard_ProcessUtility(Node *parsetree, break; case T_CreatePLangStmt: - CreateProceduralLanguage((CreatePLangStmt *) parsetree); + CreateProceduralLanguage((CreatePLangStmt *) parsetree, completionTag); break; case T_DropPLangStmt: diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h index 0843201..15e9603 100644 --- a/src/include/catalog/pg_proc_fn.h +++ b/src/include/catalog/pg_proc_fn.h @@ -37,7 +37,8 @@ extern Oid ProcedureCreate(const char *procedureName, List *parameterDefaults, Datum proconfig, float4 procost, - float4 prorows); + float4 prorows, + bool *didReplace); extern bool function_parse_error_transpose(const char *prosrc); diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 86d62af..12278e8 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -52,7 +52,7 @@ extern List *ChooseIndexColumnNames(List *indexElems); extern Oid GetDefaultOpClass(Oid type_id, Oid am_id); /* commands/functioncmds.c */ -extern void CreateFunction(CreateFunctionStmt *stmt, const char *queryString); +extern void CreateFunction(CreateFunctionStmt *stmt, const char *queryString, char *commandTag); extern void RemoveFunction(RemoveFuncStmt *stmt); extern void RemoveFunctionById(Oid funcOid); extern void SetFunctionReturnType(Oid funcOid, Oid newRetType); diff --git a/src/include/commands/proclang.h b/src/include/commands/proclang.h index aa1fb34..871ad98 100644 --- a/src/include/commands/proclang.h +++ b/src/include/commands/proclang.h @@ -14,7 +14,7 @@ #include "nodes/parsenodes.h" -extern void CreateProceduralLanguage(CreatePLangStmt *stmt); +extern void CreateProceduralLanguage(CreatePLangStmt *stmt, char *completionTag); extern void DropProceduralLanguage(DropPLangStmt *stmt); extern void DropProceduralLanguageById(Oid langOid); extern void RenameLanguage(const char *oldname, const char *newname); diff --git a/src/include/commands/view.h b/src/include/commands/view.h index cffd1a0..4d1d701 100644 --- a/src/include/commands/view.h +++ b/src/include/commands/view.h @@ -16,6 +16,6 @@ #include "nodes/parsenodes.h" -extern void DefineView(ViewStmt *stmt, const char *queryString); +extern void DefineView(ViewStmt *stmt, const char *queryString, char *completionTag); #endif /* VIEW_H */ diff --git a/src/include/rewrite/rewriteDefine.h b/src/include/rewrite/rewriteDefine.h index a739d81..dd22393 100644 --- a/src/include/rewrite/rewriteDefine.h +++ b/src/include/rewrite/rewriteDefine.h @@ -22,7 +22,7 @@ #define RULE_FIRES_ON_REPLICA 'R' #define RULE_DISABLED 'D' -extern void DefineRule(RuleStmt *stmt, const char *queryString); +extern void DefineRule(RuleStmt *stmt, const char *queryString, char *completionTag); extern void DefineQueryRewrite(char *rulename, Oid event_relid, @@ -30,7 +30,8 @@ extern void DefineQueryRewrite(char *rulename, CmdType event_type, bool is_instead, bool replace, - List *action); + List *action, + bool *didReplace); extern void RenameRewriteRule(Oid owningRel, const char *oldName, const char *newName); -- 1.7.3.2
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers