[PATCH] Return command tag 'REPLACE X' for CREATE OR REPLACE statements.
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
Attachments:
0001-Return-command-tag-REPLACE-X-for-CREATE-OR-REPLACE-s.patchtext/x-patch; charset=US-ASCII; name=0001-Return-command-tag-REPLACE-X-for-CREATE-OR-REPLACE-s.patchDownload
From b848a4129e31aa021059dab5a0a7ad139fe018b3 Mon Sep 17 00:00:00 2001
From: Marti Raudsepp <marti@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
Marti Raudsepp <marti@juffo.org> writes:
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.
This is going to break clients that expect commands to return the same
command tag as they have in the past. I doubt that whatever usefulness
is gained will outweigh the compatibility problems.
regards, tom lane
On Sun, Nov 28, 2010 at 11:29 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Marti Raudsepp <marti@juffo.org> writes:
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.This is going to break clients that expect commands to return the same
command tag as they have in the past. I doubt that whatever usefulness
is gained will outweigh the compatibility problems.
You complained about this when we changed the SELECT tag for 9.0 to
include row-counts for CTAS etc. where it hadn't before. Have we
gotten any complaints about that change breaking clients?
I think more expessive command tags are in general a good thing. The
idea that this particular change would be useful primarily for humans
examining the psql output seems a bit weak to me, but I can easily see
it being useful for programs. Right now a program has no reason to
look at this command tag anyway; it'll always be the same.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
I think more expessive command tags are in general a good thing. The
idea that this particular change would be useful primarily for humans
examining the psql output seems a bit weak to me, but I can easily see
it being useful for programs. Right now a program has no reason to
look at this command tag anyway; it'll always be the same.
Hmm ... that's a good point, although I'm not sure that it's 100% true.
regards, tom lane
I tried to pick up this patch to review.
It seems to me fine, enough simple and works as explained in the
implementation level, apart from reasonability of this feature.
(Tom was not 100% agree with this feature 1.5month ago.)
I'm not certain whether the current regression test should be
updated, or not. The pg_regress launches psql with -q option,
so completionTag is always ignored.
Thanks,
(2010/11/29 0:14), Marti Raudsepp wrote:
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
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2011/1/13 KaiGai Kohei <kaigai@ak.jp.nec.com>:
I tried to pick up this patch to review.
It seems to me fine, enough simple and works as explained in the
implementation level, apart from reasonability of this feature.
(Tom was not 100% agree with this feature 1.5month ago.)
Did you check whether this updated the code for 100% of the object
types where this could apply?
I'm not certain whether the current regression test should be
updated, or not. The pg_regress launches psql with -q option,
so completionTag is always ignored.
Well, I don't see any easy way of regression testing it, then. Am I
missing something?
Also, I don't really like the way this spreads knowledge of the
completionTag out all over the backend. I think it would be better to
follow the existing model used by the COPY and COMMIT commands,
whereby the return value indicates what happened and
standard_ProcessUtility() uses that to set the command tag.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Excerpts from Robert Haas's message of vie ene 14 08:40:07 -0300 2011:
Also, I don't really like the way this spreads knowledge of the
completionTag out all over the backend. I think it would be better to
follow the existing model used by the COPY and COMMIT commands,
whereby the return value indicates what happened and
standard_ProcessUtility() uses that to set the command tag.
Yeah, that looks ugly. However it's already ugly elsewhere: for example
see PerformPortalFetch. I am not sure if it should be this patch's
responsability to clean that stuff up. (Maybe we should decree that at
least this patch shouldn't make the situation worse.)
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On Fri, Jan 14, 2011 at 9:24 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:
Excerpts from Robert Haas's message of vie ene 14 08:40:07 -0300 2011:
Also, I don't really like the way this spreads knowledge of the
completionTag out all over the backend. I think it would be better to
follow the existing model used by the COPY and COMMIT commands,
whereby the return value indicates what happened and
standard_ProcessUtility() uses that to set the command tag.Yeah, that looks ugly. However it's already ugly elsewhere: for example
see PerformPortalFetch. I am not sure if it should be this patch's
responsability to clean that stuff up. (Maybe we should decree that at
least this patch shouldn't make the situation worse.)
Agreed: it's not the patch's job to clean it up, but it shouldn't make
the situation worse.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Alvaro Herrera <alvherre@commandprompt.com> writes:
Excerpts from Robert Haas's message of vie ene 14 08:40:07 -0300 2011:
Also, I don't really like the way this spreads knowledge of the
completionTag out all over the backend. I think it would be better to
follow the existing model used by the COPY and COMMIT commands,
whereby the return value indicates what happened and
standard_ProcessUtility() uses that to set the command tag.
Yeah, that looks ugly. However it's already ugly elsewhere: for example
see PerformPortalFetch. I am not sure if it should be this patch's
responsability to clean that stuff up. (Maybe we should decree that at
least this patch shouldn't make the situation worse.)
I thought we were going to reject the patch outright anyway. The
compatibility consequences of changing command tags are not worth the
benefit, independently of how ugly the backend-side code may or may
not be.
regards, tom lane
On Fri, 2011-01-14 at 12:07 -0500, Tom Lane wrote:
I thought we were going to reject the patch outright anyway. The
compatibility consequences of changing command tags are not worth the
benefit, independently of how ugly the backend-side code may or may
not be.
+1
--
Simon Riggs http://www.2ndQuadrant.com/books/
PostgreSQL Development, 24x7 Support, Training and Services
On Fri, Jan 14, 2011 at 12:07 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Alvaro Herrera <alvherre@commandprompt.com> writes:
Excerpts from Robert Haas's message of vie ene 14 08:40:07 -0300 2011:
Also, I don't really like the way this spreads knowledge of the
completionTag out all over the backend. I think it would be better to
follow the existing model used by the COPY and COMMIT commands,
whereby the return value indicates what happened and
standard_ProcessUtility() uses that to set the command tag.Yeah, that looks ugly. However it's already ugly elsewhere: for example
see PerformPortalFetch. I am not sure if it should be this patch's
responsability to clean that stuff up. (Maybe we should decree that at
least this patch shouldn't make the situation worse.)I thought we were going to reject the patch outright anyway. The
compatibility consequences of changing command tags are not worth the
benefit, independently of how ugly the backend-side code may or may
not be.
My previous response to this criticism was here:
http://archives.postgresql.org/pgsql-hackers/2010-11/msg01899.php
Your response, which seemed at least partially in agreement, is here:
http://archives.postgresql.org/pgsql-hackers/2010-11/msg01901.php
If we're going to reject this patch on backwards-compatibility
grounds, we need to make an argument that the backward-compatibility
hazards are a real concern. So, again, has anyone complained about
the changes we made in this area in 9.0? And under what circumstances
do we foresee someone relying on the command tag of a command that
always returns the same tag? I'm as quick as anyone to bow before a
compelling argument, but I don't think anyone's made such an argument.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
If we're going to reject this patch on backwards-compatibility
grounds, we need to make an argument that the backward-compatibility
hazards are a real concern. So, again, has anyone complained about
the changes we made in this area in 9.0?
That 9.0 change was far less invasive than this: it only added a count
field to SELECT and CTAS result tags. Quite aside from the fact that
the tag name stayed the same, in the SELECT case it's unlikely anyone
would have checked the tag at all rather than just testing for
PQresultStatus() == PGRES_TUPLES_OK. So it was basically only changing
the result for *one* command type. I don't think it's a good basis for
arguing that this patch won't cause problems.
regards, tom lane
On Fri, Jan 14, 2011 at 1:35 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
If we're going to reject this patch on backwards-compatibility
grounds, we need to make an argument that the backward-compatibility
hazards are a real concern. So, again, has anyone complained about
the changes we made in this area in 9.0?That 9.0 change was far less invasive than this: it only added a count
field to SELECT and CTAS result tags. Quite aside from the fact that
the tag name stayed the same, in the SELECT case it's unlikely anyone
would have checked the tag at all rather than just testing for
PQresultStatus() == PGRES_TUPLES_OK. So it was basically only changing
the result for *one* command type. I don't think it's a good basis for
arguing that this patch won't cause problems.
Yeah, but that one command tag was SELECT. That's a pretty commonly
used command. Most production environments probably use all of the
commands affected by this patch together an order of magnitude less
often than they use SELECT.
Again, on what basis are we arguing that people are going to be
looking at the command tag of a command that always returns the same
tag? That seems pretty darn unlikely to me.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
On Fri, Jan 14, 2011 at 1:35 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
That 9.0 change was far less invasive than this: it only added a count
field to SELECT and CTAS result tags. �Quite aside from the fact that
the tag name stayed the same, in the SELECT case it's unlikely anyone
would have checked the tag at all rather than just testing for
PQresultStatus() == PGRES_TUPLES_OK.
Yeah, but that one command tag was SELECT. That's a pretty commonly
used command.
You're ignoring the point that people would probably use PQresultStatus
in preference to checking the tag at all, when dealing with SELECT.
psql itself is an example --- it never looks at the tag, nor shows it to
the user, in the SELECT case. That patch only really changed the
exposed behavior for CREATE TABLE AS SELECT / SELECT INTO, neither of
which can be claimed to be hugely popular things for programs to issue.
The other side of the argument that needs to be considered is what the
benefit is. There was a fairly clear functional gain from reporting
the rowcount for CTAS. I'm less convinced that sending back REPLACE
is a big benefit worth taking big compatibility risks for.
regards, tom lane
On Fri, Jan 14, 2011 at 2:07 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Fri, Jan 14, 2011 at 1:35 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
That 9.0 change was far less invasive than this: it only added a count
field to SELECT and CTAS result tags. Quite aside from the fact that
the tag name stayed the same, in the SELECT case it's unlikely anyone
would have checked the tag at all rather than just testing for
PQresultStatus() == PGRES_TUPLES_OK.Yeah, but that one command tag was SELECT. That's a pretty commonly
used command.You're ignoring the point that people would probably use PQresultStatus
in preference to checking the tag at all, when dealing with SELECT.
I would assume they would also use PQresultStatus() when checking
whether CREATE OR REPLACE FUNCTION worked. Even if they were using
PQcmdStatus() for some reason, which seems like an odd thing to do,
there'd be no reason to check for anything beyond "is it empty?". The
idea that there are massive amounts of code out there that are
expecting the command tag to be *exactly* CREATE FUNCTION and will
break if it differs by a byte seems quite improbable. Can you produce
an example of any such code?
The other side of the argument that needs to be considered is what the
benefit is. There was a fairly clear functional gain from reporting
the rowcount for CTAS. I'm less convinced that sending back REPLACE
is a big benefit worth taking big compatibility risks for.
Asserting that there are "big compatibility risks" doesn't make it so
- you've offered no evidence of that. Even if a handful of people had
complained about that one, I would still felt it was a good change,
but it doesn't seem that there are any at all. I classify this as one
of a dozen or two minor usability enhancements that we make in every
release, and most people don't care, and those who do go "oh, that's
handy". I think before we reject a patch for breaking things, we
ought to be able to identify either some actual application that is
broken by it, or at least some reasonably plausible coding pattern
that would blow up.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Thanks for reviewing!
On Fri, Jan 14, 2011 at 13:40, Robert Haas <robertmhaas@gmail.com> wrote:
Did you check whether this updated the code for 100% of the object
types where this could apply?
I walked through all the CREATE statements in the documentation and
these four seem to be the only ones that accept FOR REPLACE.
There's a similar case with CREATE TABLE IF NOT EXISTS, maybe this is
worth covering in an updated patch too?
And if I change that, people might expect the same from DROP X IF EXISTS too?
Also, I don't really like the way this spreads knowledge of the
completionTag out all over the backend. I think it would be better to
follow the existing model used by the COPY and COMMIT commands,
whereby the return value indicates what happened and
standard_ProcessUtility() uses that to set the command tag.
Right. I created this pattern after PerformPortalFetch() which already
took a completionTag argument. But your approach seems more
reasonable.
Regards,
Marti
On Fri, Jan 14, 2011 at 3:45 PM, Marti Raudsepp <marti@juffo.org> wrote:
There's a similar case with CREATE TABLE IF NOT EXISTS, maybe this is
worth covering in an updated patch too?
And if I change that, people might expect the same from DROP X IF EXISTS too?
It's far less clear what you'd change those cases to say, and they
already emit a NOTICE, so it seems unnecessary.
Also, I don't really like the way this spreads knowledge of the
completionTag out all over the backend. I think it would be better to
follow the existing model used by the COPY and COMMIT commands,
whereby the return value indicates what happened and
standard_ProcessUtility() uses that to set the command tag.Right. I created this pattern after PerformPortalFetch() which already
took a completionTag argument. But your approach seems more
reasonable.
OK.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Here's an updated patch that reports command status back to
ProcessUtility via 'bool' return value.
I was a bit unsure about using bool return values because it's not
immediately obvious what "true" or "false" refer to, but defining a
new enum seemed like overkill, so I went with bool anyway. Any better
ideas?
The 2nd patch also moves MOVE/FETCH command tag formatting up to
ProcessUtility, hopefully this change is for the better.
Regards,
Marti
Attachments:
0001-Return-command-tag-REPLACE-X-for-CREATE-OR-REPLACE-s.patchtext/x-patch; charset=US-ASCII; name=0001-Return-command-tag-REPLACE-X-for-CREATE-OR-REPLACE-s.patchDownload
From 25a19ca972e6f02ba350b6e4112935ff1ed44b24 Mon Sep 17 00:00:00 2001
From: Marti Raudsepp <marti@juffo.org>
Date: Sun, 28 Nov 2010 16:49:41 +0200
Subject: [PATCH 1/2] Return command tag 'REPLACE X' for CREATE OR REPLACE statements.
This affects CREATE OR REPLACE LANGUAGE/VIEW/RULE/FUNCTION
Related functions that previously returned "void" have been changed to
"bool" to report status up. Others have a new argument bool *didReplace.
---
src/backend/catalog/pg_aggregate.c | 3 +-
src/backend/catalog/pg_proc.c | 6 ++++-
src/backend/commands/functioncmds.c | 13 ++++++++--
src/backend/commands/proclang.c | 34 ++++++++++++++++++---------
src/backend/commands/view.c | 22 +++++++++--------
src/backend/rewrite/rewriteDefine.c | 33 ++++++++++++++++++---------
src/backend/tcop/utility.c | 43 +++++++++++++++++++++++++++++++---
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 | 4 +-
12 files changed, 119 insertions(+), 48 deletions(-)
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 86e8c6b..95e4469 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 2ab87d2..8b15efc 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 2a2b7c7..4678d9c 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -768,8 +768,11 @@ interpret_AS_clause(Oid languageOid, const char *languageName,
/*
* CreateFunction
* Execute a CREATE FUNCTION utility statement.
+ *
+ * Returns TRUE if function was replaced via "CREATE OR REPLACE", FALSE
+ * if created.
*/
-void
+bool
CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
{
char *probin_str;
@@ -791,7 +794,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
Oid requiredResultType;
bool isWindowFunc,
isStrict,
- security;
+ security,
+ didReplace;
char volatility;
ArrayType *proconfig;
float4 procost;
@@ -958,7 +962,10 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
parameterDefaults,
PointerGetDatum(proconfig),
procost,
- prorows);
+ prorows,
+ &didReplace);
+
+ return didReplace;
}
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 3860105..7a1520c 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -49,7 +49,7 @@ typedef struct
char *tmpllibrary; /* path of shared library */
} PLTemplate;
-static void create_proc_lang(const char *languageName, bool replace,
+static bool create_proc_lang(const char *languageName, bool replace,
Oid languageOwner, Oid handlerOid, Oid inlineOid,
Oid valOid, bool trusted);
static PLTemplate *find_language_template(const char *languageName);
@@ -59,9 +59,12 @@ static void AlterLanguageOwner_internal(HeapTuple tup, Relation rel,
/* ---------------------------------------------------------------------
* CREATE PROCEDURAL LANGUAGE
+ *
+ * Returns TRUE if language was replaced via "CREATE OR REPLACE", FALSE
+ * if created.
* ---------------------------------------------------------------------
*/
-void
+bool
CreateProceduralLanguage(CreatePLangStmt *stmt)
{
char *languageName;
@@ -146,7 +149,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
NIL,
PointerGetDatum(NULL),
1,
- 0);
+ 0,
+ NULL);
}
/*
@@ -181,7 +185,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
NIL,
PointerGetDatum(NULL),
1,
- 0);
+ 0,
+ NULL);
}
}
else
@@ -219,16 +224,17 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
NIL,
PointerGetDatum(NULL),
1,
- 0);
+ 0,
+ NULL);
}
}
else
valOid = InvalidOid;
/* ok, create it */
- create_proc_lang(languageName, stmt->replace, GetUserId(),
- handlerOid, inlineOid,
- valOid, pltemplate->tmpltrusted);
+ return create_proc_lang(languageName, stmt->replace, GetUserId(),
+ handlerOid, inlineOid,
+ valOid, pltemplate->tmpltrusted);
}
else
{
@@ -301,16 +307,18 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
valOid = InvalidOid;
/* ok, create it */
- create_proc_lang(languageName, stmt->replace, GetUserId(),
- handlerOid, inlineOid,
- valOid, stmt->pltrusted);
+ return create_proc_lang(languageName, stmt->replace, GetUserId(),
+ handlerOid, inlineOid,
+ valOid, stmt->pltrusted);
}
}
/*
* Guts of language creation.
+ *
+ * Returns TRUE if function was replaced, FALSE otherwise.
*/
-static void
+static bool
create_proc_lang(const char *languageName, bool replace,
Oid languageOwner, Oid handlerOid, Oid inlineOid,
Oid valOid, bool trusted)
@@ -431,6 +439,8 @@ create_proc_lang(const char *languageName, bool replace,
LanguageRelationId, myself.objectId, 0);
heap_close(rel, RowExclusiveLock);
+
+ return is_update;
}
/*
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index a684172..41d4321 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -303,20 +303,20 @@ checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
*/
}
-static void
+static bool
DefineViewRules(Oid viewOid, Query *viewParse, bool replace)
{
/*
* Set up the ON SELECT rule. Since the query has already been through
* parse analysis, we use DefineQueryRewrite() directly.
*/
- DefineQueryRewrite(pstrdup(ViewSelectRuleName),
- viewOid,
- NULL,
- CMD_SELECT,
- true,
- replace,
- list_make1(viewParse));
+ return DefineQueryRewrite(pstrdup(ViewSelectRuleName),
+ viewOid,
+ NULL,
+ CMD_SELECT,
+ true,
+ replace,
+ list_make1(viewParse));
/*
* Someday: automatic ON INSERT, etc
@@ -392,8 +392,10 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
/*
* DefineView
* Execute a CREATE VIEW command.
+ *
+ * Returns TRUE if view was replaced via "CREATE OR REPLACE"
*/
-void
+bool
DefineView(ViewStmt *stmt, const char *queryString)
{
Query *viewParse;
@@ -489,5 +491,5 @@ DefineView(ViewStmt *stmt, const char *queryString)
/*
* Now create the rules associated with the view.
*/
- DefineViewRules(viewOid, viewParse, stmt->replace);
+ return DefineViewRules(viewOid, viewParse, stmt->replace);
}
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 99c397b..518154e 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,14 +185,18 @@ InsertRule(char *rulname,
heap_close(pg_rewrite_desc, RowExclusiveLock);
+ *didReplace = is_update;
+
return rewriteObjectId;
}
/*
* DefineRule
* Execute a CREATE RULE command.
+ *
+ * Returns TRUE if rule was replaced via "CREATE OR REPLACE".
*/
-void
+bool
DefineRule(RuleStmt *stmt, const char *queryString)
{
List *actions;
@@ -205,13 +210,13 @@ DefineRule(RuleStmt *stmt, const char *queryString)
relId = RangeVarGetRelid(stmt->relation, false);
/* ... and execute */
- DefineQueryRewrite(stmt->rulename,
- relId,
- whereClause,
- stmt->event,
- stmt->instead,
- stmt->replace,
- actions);
+ return DefineQueryRewrite(stmt->rulename,
+ relId,
+ whereClause,
+ stmt->event,
+ stmt->instead,
+ stmt->replace,
+ actions);
}
@@ -221,8 +226,10 @@ DefineRule(RuleStmt *stmt, const char *queryString)
*
* This is essentially the same as DefineRule() except that the rule's
* action and qual have already been passed through parse analysis.
+ *
+ * Returns TRUE if rule was replaced, FALSE otherwise.
*/
-void
+bool
DefineQueryRewrite(char *rulename,
Oid event_relid,
Node *event_qual,
@@ -237,6 +244,7 @@ DefineQueryRewrite(char *rulename,
ListCell *l;
Query *query;
bool RelisBecomingView = false;
+ bool didReplace = false;
/*
* If we are installing an ON SELECT rule, we had better grab
@@ -487,7 +495,8 @@ DefineQueryRewrite(char *rulename,
is_instead,
event_qual,
action,
- replace);
+ replace,
+ &didReplace);
/*
* Set pg_class 'relhasrules' field TRUE for event relation. If
@@ -512,6 +521,8 @@ DefineQueryRewrite(char *rulename,
/* Close rel, but keep lock till commit... */
heap_close(event_relation, NoLock);
+
+ return didReplace;
}
/*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 9500037..b1120d0 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -336,6 +336,8 @@ standard_ProcessUtility(Node *parsetree,
DestReceiver *dest,
char *completionTag)
{
+ bool didReplace;
+
check_xact_readonly(parsetree);
if (completionTag)
@@ -891,11 +893,28 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_ViewStmt: /* CREATE VIEW */
- DefineView((ViewStmt *) parsetree, queryString);
+ didReplace = DefineView((ViewStmt *) parsetree, queryString);
+
+ if (completionTag)
+ {
+ if (didReplace)
+ strcpy(completionTag, "REPLACE VIEW");
+ else
+ strcpy(completionTag, "CREATE VIEW");
+ }
break;
case T_CreateFunctionStmt: /* CREATE FUNCTION */
- CreateFunction((CreateFunctionStmt *) parsetree, queryString);
+ didReplace = CreateFunction((CreateFunctionStmt *) parsetree,
+ queryString);
+
+ if (completionTag)
+ {
+ if (didReplace)
+ strcpy(completionTag, "REPLACE FUNCTION");
+ else
+ strcpy(completionTag, "CREATE FUNCTION");
+ }
break;
case T_AlterFunctionStmt: /* ALTER FUNCTION */
@@ -939,7 +958,15 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_RuleStmt: /* CREATE RULE */
- DefineRule((RuleStmt *) parsetree, queryString);
+ didReplace = DefineRule((RuleStmt *) parsetree, queryString);
+
+ if (completionTag)
+ {
+ if (didReplace)
+ strcpy(completionTag, "REPLACE RULE");
+ else
+ strcpy(completionTag, "CREATE RULE");
+ }
break;
case T_CreateSeqStmt:
@@ -1110,7 +1137,15 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_CreatePLangStmt:
- CreateProceduralLanguage((CreatePLangStmt *) parsetree);
+ didReplace = CreateProceduralLanguage((CreatePLangStmt *) parsetree);
+
+ if (completionTag)
+ {
+ if (didReplace)
+ strcpy(completionTag, "REPLACE LANGUAGE");
+ else
+ strcpy(completionTag, "CREATE LANGUAGE");
+ }
break;
case T_DropPLangStmt:
diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h
index 09d779f..2e354b7 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 a806f9e..530cc74 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 bool CreateFunction(CreateFunctionStmt *stmt, const char *queryString);
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..be91f3b 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 bool CreateProceduralLanguage(CreatePLangStmt *stmt);
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 9751e32..f58122c 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 bool DefineView(ViewStmt *stmt, const char *queryString);
#endif /* VIEW_H */
diff --git a/src/include/rewrite/rewriteDefine.h b/src/include/rewrite/rewriteDefine.h
index 4bf474b..052c002 100644
--- a/src/include/rewrite/rewriteDefine.h
+++ b/src/include/rewrite/rewriteDefine.h
@@ -22,9 +22,9 @@
#define RULE_FIRES_ON_REPLICA 'R'
#define RULE_DISABLED 'D'
-extern void DefineRule(RuleStmt *stmt, const char *queryString);
+extern bool DefineRule(RuleStmt *stmt, const char *queryString);
-extern void DefineQueryRewrite(char *rulename,
+extern bool DefineQueryRewrite(char *rulename,
Oid event_relid,
Node *event_qual,
CmdType event_type,
--
1.7.3.5
0002-Move-MOVE-FETCH-command-tag-formatting-to-ProcessUti.patchtext/x-patch; charset=US-ASCII; name=0002-Move-MOVE-FETCH-command-tag-formatting-to-ProcessUti.patchDownload
From d11dc0f652277874e79a6eee4ece346e6978eea0 Mon Sep 17 00:00:00 2001
From: Marti Raudsepp <marti@juffo.org>
Date: Sat, 15 Jan 2011 22:55:28 +0200
Subject: [PATCH 2/2] Move MOVE/FETCH command tag formatting to ProcessUtility.
---
src/backend/commands/portalcmds.c | 20 ++++----------------
src/backend/tcop/utility.c | 13 +++++++++++--
src/include/commands/portalcmds.h | 3 +--
3 files changed, 16 insertions(+), 20 deletions(-)
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index dda592c..80b581d 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -137,15 +137,12 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
*
* stmt: parsetree node for command
* dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
*
- * completionTag may be NULL if caller doesn't want a status string.
+ * Returns number of rows processed.
*/
-void
+long
PerformPortalFetch(FetchStmt *stmt,
- DestReceiver *dest,
- char *completionTag)
+ DestReceiver *dest)
{
Portal portal;
long nprocessed;
@@ -174,16 +171,7 @@ PerformPortalFetch(FetchStmt *stmt,
dest = None_Receiver;
/* Do it */
- nprocessed = PortalRunFetch(portal,
- stmt->direction,
- stmt->howMany,
- dest);
-
- /* Return command status if wanted */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
- stmt->ismove ? "MOVE" : "FETCH",
- nprocessed);
+ return PortalRunFetch(portal, stmt->direction, stmt->howMany, dest);
}
/*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b1120d0..40c9a02 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -482,8 +482,17 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_FetchStmt:
- PerformPortalFetch((FetchStmt *) parsetree, dest,
- completionTag);
+ {
+ FetchStmt *stmt = (FetchStmt *) parsetree;
+ long nprocessed;
+
+ nprocessed = PerformPortalFetch(stmt, dest);
+
+ if (completionTag)
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
+ stmt->ismove ? "MOVE %ld" : "FETCH %ld",
+ nprocessed);
+ }
break;
/*
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index c64aabf..6afd737 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.h
@@ -22,8 +22,7 @@
extern void PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
const char *queryString, bool isTopLevel);
-extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
- char *completionTag);
+extern long PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest);
extern void PerformPortalClose(const char *name);
--
1.7.3.5
On fre, 2011-01-14 at 18:45 -0500, Robert Haas wrote:
On Fri, Jan 14, 2011 at 3:45 PM, Marti Raudsepp <marti@juffo.org>
wrote:There's a similar case with CREATE TABLE IF NOT EXISTS, maybe this
is
worth covering in an updated patch too?
And if I change that, people might expect the same from DROP X IFEXISTS too?
It's far less clear what you'd change those cases to say, and they
already emit a NOTICE, so it seems unnecessary.
Maybe instead of the proposed patch, a notice could be added:
NOTICE: existing object was replaced
On Mon, Jan 17, 2011 at 9:41 AM, Peter Eisentraut <peter_e@gmx.net> wrote:
On fre, 2011-01-14 at 18:45 -0500, Robert Haas wrote:
On Fri, Jan 14, 2011 at 3:45 PM, Marti Raudsepp <marti@juffo.org>
wrote:There's a similar case with CREATE TABLE IF NOT EXISTS, maybe this
is
worth covering in an updated patch too?
And if I change that, people might expect the same from DROP X IFEXISTS too?
It's far less clear what you'd change those cases to say, and they
already emit a NOTICE, so it seems unnecessary.Maybe instead of the proposed patch, a notice could be added:
NOTICE: existing object was replaced
Well, that would eliminate the backward-compatibility hazard, pretty
much, but it seems noisy. I already find some of these notices to be
unduly informative.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On mån, 2011-01-17 at 10:05 -0500, Robert Haas wrote:
On Mon, Jan 17, 2011 at 9:41 AM, Peter Eisentraut <peter_e@gmx.net> wrote:
On fre, 2011-01-14 at 18:45 -0500, Robert Haas wrote:
On Fri, Jan 14, 2011 at 3:45 PM, Marti Raudsepp <marti@juffo.org>
wrote:There's a similar case with CREATE TABLE IF NOT EXISTS, maybe this
is
worth covering in an updated patch too?
And if I change that, people might expect the same from DROP X IFEXISTS too?
It's far less clear what you'd change those cases to say, and they
already emit a NOTICE, so it seems unnecessary.Maybe instead of the proposed patch, a notice could be added:
NOTICE: existing object was replaced
Well, that would eliminate the backward-compatibility hazard, pretty
much, but it seems noisy. I already find some of these notices to be
unduly informative.
I'm also anti-NOTICE.
I'm just saying, we propose that CREATE OR REPLACE should return a tag
of CREATE or REPLACE depending on what it did, then DROP IF NOT EXISTS
should also return a tag of DROP or ??? depending on what it did. Since
the latter question was settled by a notice, that would also be the
proper answer for the former.
Perhaps the next thing is that MERGE should return INSERT or UPDATE
depending on the outcome?
Robert Haas <robertmhaas@gmail.com> writes:
On Mon, Jan 17, 2011 at 9:41 AM, Peter Eisentraut <peter_e@gmx.net> wrote:
Maybe instead of the proposed patch, a notice could be added:
NOTICE: existing object was replaced
Well, that would eliminate the backward-compatibility hazard, pretty
much, but it seems noisy. I already find some of these notices to be
unduly informative.
ROTFL ...
There has been some previous banter about reorganizing or reclassifying
the various NOTICE messages to make them more useful and/or less noisy;
but I don't think we've ever had a really concrete proposal for better
behavior. Maybe it's time to reopen that discussion.
I do agree with Peter's underlying point: it would be pretty
inconsistent for CREATE OR REPLACE to report this bit of info via
command tag when CREATE IF NOT EXISTS is reporting an absolutely
equivalent bit of info via elog(NOTICE).
regards, tom lane
On Mon, Jan 17, 2011 at 09:23:07PM +0200, Peter Eisentraut wrote:
On m�n, 2011-01-17 at 10:05 -0500, Robert Haas wrote:
On Mon, Jan 17, 2011 at 9:41 AM, Peter Eisentraut <peter_e@gmx.net> wrote:
On fre, 2011-01-14 at 18:45 -0500, Robert Haas wrote:
On Fri, Jan 14, 2011 at 3:45 PM, Marti Raudsepp <marti@juffo.org>
wrote:There's a similar case with CREATE TABLE IF NOT EXISTS, maybe this
is
worth covering in an updated patch too?
And if I change that, people might expect the same from DROP X IFEXISTS too?
It's far less clear what you'd change those cases to say, and they
already emit a NOTICE, so it seems unnecessary.Maybe instead of the proposed patch, a notice could be added:
NOTICE: existing object was replaced
Well, that would eliminate the backward-compatibility hazard, pretty
much, but it seems noisy. I already find some of these notices to be
unduly informative.I'm also anti-NOTICE.
I'm just saying, we propose that CREATE OR REPLACE should return a tag
of CREATE or REPLACE depending on what it did, then DROP IF NOT EXISTS
should also return a tag of DROP or ??? depending on what it did. Since
the latter question was settled by a notice, that would also be the
proper answer for the former.Perhaps the next thing is that MERGE should return INSERT or UPDATE
depending on the outcome?
Given that it can do both in a single statement, I'm guessing that
this is intended to be facetious. Or are you suggesting that the
command tags become an array? This has all kinds of interesting
possibilities, but would of course break all kinds of stuff in the
process.
Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
On Mon, Jan 17, 2011 at 2:25 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Mon, Jan 17, 2011 at 9:41 AM, Peter Eisentraut <peter_e@gmx.net> wrote:
Maybe instead of the proposed patch, a notice could be added:
NOTICE: existing object was replacedWell, that would eliminate the backward-compatibility hazard, pretty
much, but it seems noisy. I already find some of these notices to be
unduly informative.ROTFL ...
There has been some previous banter about reorganizing or reclassifying
the various NOTICE messages to make them more useful and/or less noisy;
but I don't think we've ever had a really concrete proposal for better
behavior. Maybe it's time to reopen that discussion.I do agree with Peter's underlying point: it would be pretty
inconsistent for CREATE OR REPLACE to report this bit of info via
command tag when CREATE IF NOT EXISTS is reporting an absolutely
equivalent bit of info via elog(NOTICE).
There's a fine line between serious discussion, humor, and outright
mockery here, and I'm not too sure which one Peter's currently engaged
in. I guess the point here for me is that commands tags for SELECT,
INSERT, UPDATE, and DELETE all return some useful information about
what actually happened - especially, a row count. If it's reasonable
for those commands to return a row count in the command tag, then
there's no reason why utility commands shouldn't also be allowed to
return high-level status information as part of the command tag. On
the flip side we could just rip out command tags altogether and have
psql print out the first two words of the input string.
The asymmetry between DROP-IF-EXISTS and CREATE-IF-NOT-EXISTS and the
proposed CREATE-OR-REPLACE behavior doesn't bother me very much,
because it's already asymmetric: the first two currently report what
happened, and the third one currently doesn't. If you want to propose
to make all of them consistent, how? I don't particularly like the
idea of adding a NOTICE here; we've got too many of those already[1]rhaas=# create table foo (a serial primary key); NOTICE: CREATE TABLE will create implicit sequence "foo_a_seq" for serial column "foo.a" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo" CREATE TABLE.
Making DIE report that it didn't do anything via a command tag clearly
won't work, because you can say "DROP IF EXISTS foo, bar, baz" and the
answer might not be the same in all three cases, but COR has no such
issue.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
[1]: rhaas=# create table foo (a serial primary key); NOTICE: CREATE TABLE will create implicit sequence "foo_a_seq" for serial column "foo.a" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo" CREATE TABLE
NOTICE: CREATE TABLE will create implicit sequence "foo_a_seq" for
serial column "foo.a"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index
"foo_pkey" for table "foo"
CREATE TABLE
Well, yeah, why did I say primary key if I didn't want a primary key
index to be created, and why did I say serial if I didn't want a
sequence?
On Mon, Jan 17, 2011 at 2:52 PM, Robert Haas <robertmhaas@gmail.com> wrote:
On Mon, Jan 17, 2011 at 2:25 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Mon, Jan 17, 2011 at 9:41 AM, Peter Eisentraut <peter_e@gmx.net> wrote:
Maybe instead of the proposed patch, a notice could be added:
NOTICE: existing object was replacedWell, that would eliminate the backward-compatibility hazard, pretty
much, but it seems noisy. I already find some of these notices to be
unduly informative.ROTFL ...
There has been some previous banter about reorganizing or reclassifying
the various NOTICE messages to make them more useful and/or less noisy;
but I don't think we've ever had a really concrete proposal for better
behavior. Maybe it's time to reopen that discussion.I do agree with Peter's underlying point: it would be pretty
inconsistent for CREATE OR REPLACE to report this bit of info via
command tag when CREATE IF NOT EXISTS is reporting an absolutely
equivalent bit of info via elog(NOTICE).There's a fine line between serious discussion, humor, and outright
mockery here, and I'm not too sure which one Peter's currently engaged
in. I guess the point here for me is that commands tags for SELECT,
INSERT, UPDATE, and DELETE all return some useful information about
what actually happened - especially, a row count. If it's reasonable
for those commands to return a row count in the command tag, then
there's no reason why utility commands shouldn't also be allowed to
return high-level status information as part of the command tag. On
the flip side we could just rip out command tags altogether and have
psql print out the first two words of the input string.The asymmetry between DROP-IF-EXISTS and CREATE-IF-NOT-EXISTS and the
proposed CREATE-OR-REPLACE behavior doesn't bother me very much,
because it's already asymmetric: the first two currently report what
happened, and the third one currently doesn't. If you want to propose
to make all of them consistent, how? I don't particularly like the
idea of adding a NOTICE here; we've got too many of those already[1].
Making DIE report that it didn't do anything via a command tag clearly
won't work, because you can say "DROP IF EXISTS foo, bar, baz" and the
answer might not be the same in all three cases, but COR has no such
issue.
It seems no one wants to put any further effort into this problem. Bummer.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Sat, Jan 22, 2011 at 9:11 PM, Robert Haas <robertmhaas@gmail.com> wrote:
It seems no one wants to put any further effort into this problem. Bummer.
Since no one has felt the need to dispute the above statement in the
last 6 days, it seems clear to mark this Returned with Feedback, which
I have now done.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Fri, Jan 28, 2011 at 21:04, Robert Haas <robertmhaas@gmail.com> wrote:
On Sat, Jan 22, 2011 at 9:11 PM, Robert Haas <robertmhaas@gmail.com> wrote:
It seems no one wants to put any further effort into this problem. Bummer.
Since no one has felt the need to dispute the above statement in the
last 6 days, it seems clear to mark this Returned with Feedback, which
I have now done.
Just to be clear, I could put more effort into this, but I agree with
your concerns about this introducing inconsistency. Given how few
people are interested in this change, it's probably best to drop it.
Regards,
Marti