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

Reply via email to