diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml
index 7e0f4318bf..7e1ff18d5e 100644
--- a/doc/src/sgml/ref/comment.sgml
+++ b/doc/src/sgml/ref/comment.sgml
@@ -1,5 +1,5 @@
@@ -25,12 +25,17 @@ COMMENT ON
TABLE object_name |
COLUMN table_name.column_name |
AGGREGATE agg_name (agg_type) |
+ CAST (sourcetype AS targettype) |
CONSTRAINT constraint_name ON table_name |
+ CONVERSION object_name |
DATABASE object_name |
DOMAIN object_name |
FUNCTION func_name (arg1_type, arg2_type, ...) |
INDEX object_name |
+ LARGE OBJECT large_object_oid |
OPERATOR op (leftoperand_type, rightoperand_type) |
+ OPERATOR CLASS object_name USING index_method |
+ [ PROCEDURAL ] LANGUAGE object_name |
RULE rule_name ON table_name |
SCHEMA object_name |
SEQUENCE object_name |
@@ -70,7 +75,7 @@ COMMENT ON
object_name
table_name.column_name
- aggname
+ agg_name
constraint_name
func_name
op
@@ -78,13 +83,60 @@ COMMENT ON
trigger_name
- The name of the object to be be commented. Names of tables,
- aggregates, domains, functions, indexes, operators, sequences,
- types, and views may be schema-qualified.
+ The name of the object to be commented. Names of tables,
+ aggregates, domains, functions, indexes, operators, operator classes,
+ sequences, types, and views may be schema-qualified.
+
+ agg_type
+
+
+ The argument data type of the aggregate function, or
+ * if the function accepts any data type.
+
+
+
+
+
+ large_object_oid
+
+
+ The OID of the large object.
+
+
+
+
+
+ PROCEDURAL
+
+
+
+ This is a noise word.
+
+
+
+
+
+ sourcetype
+
+
+ The name of the source data type of the cast.
+
+
+
+
+
+ targettype
+
+
+ The name of the target data type of the cast.
+
+
+
+
text
@@ -93,12 +145,18 @@ COMMENT ON
+
Notes
+
+ A comment for a database can only be created in that database,
+ and will only be visible in that database, not in other databases.
+
+
There is presently no security mechanism for comments: any user
connected to a database can see all the comments for objects in
@@ -130,13 +188,18 @@ COMMENT ON TABLE mytable IS NULL;
COMMENT ON AGGREGATE my_aggregate (double precision) IS 'Computes sample variance';
+COMMENT ON CAST (text AS int4) IS 'Allow casts from text to int4';
COMMENT ON COLUMN my_table.my_column IS 'Employee ID number';
+COMMENT ON CONVERSION my_conv IS 'Conversion to Unicode';
COMMENT ON DATABASE my_database IS 'Development Database';
COMMENT ON DOMAIN my_domain IS 'Email Address Domain';
COMMENT ON FUNCTION my_function (timestamp) IS 'Returns Roman Numeral';
COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee ID';
+COMMENT ON LANGUAGE plpython IS 'Python support for stored procedures';
+COMMENT ON LARGE OBJECT 346344 IS 'Planning document';
COMMENT ON OPERATOR ^ (text, text) IS 'Performs intersection of two texts';
COMMENT ON OPERATOR ^ (NONE, text) IS 'This is a prefix operator on text';
+COMMENT ON OPERATOR CLASS int4ops USING btree IS '4 byte integer operators for btrees';
COMMENT ON RULE my_rule ON my_table IS 'Logs updates of employee records';
COMMENT ON SCHEMA my_schema IS 'Departmental data';
COMMENT ON SEQUENCE my_sequence IS 'Used to generate primary keys';
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 7534750c99..93103c16f8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.93 2003/11/12 21:15:48 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.94 2003/11/21 22:32:48 tgl Exp $
*
* NOTES
* See acl.h.
@@ -22,6 +22,7 @@
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
+#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_group.h"
#include "catalog/pg_language.h"
@@ -1551,3 +1552,31 @@ pg_database_ownercheck(Oid db_oid, AclId userid)
return userid == dba;
}
+
+/*
+ * Ownership check for a conversion (specified by OID).
+ */
+bool
+pg_conversion_ownercheck(Oid conv_oid, AclId userid)
+{
+ HeapTuple tuple;
+ AclId owner_id;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(userid))
+ return true;
+
+ tuple = SearchSysCache(CONOID,
+ ObjectIdGetDatum(conv_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("conversion with OID %u does not exist", conv_oid)));
+
+ owner_id = ((Form_pg_conversion) GETSTRUCT(tuple))->conowner;
+
+ ReleaseSysCache(tuple);
+
+ return userid == owner_id;
+}
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index 62765a96e0..2283c56379 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -7,7 +7,7 @@
* Copyright (c) 1996-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.73 2003/11/12 21:15:49 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.74 2003/11/21 22:32:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,6 +21,7 @@
#include "catalog/namespace.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_description.h"
+#include "catalog/pg_largeobject.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_trigger.h"
@@ -58,6 +59,11 @@ static void CommentProc(List *function, List *arguments, char *comment);
static void CommentOperator(List *opername, List *arguments, char *comment);
static void CommentTrigger(List *qualname, char *comment);
static void CommentConstraint(List *qualname, char *comment);
+static void CommentConversion(List *qualname, char *comment);
+static void CommentLanguage(List *qualname, char *comment);
+static void CommentOpClass(List *qualname, List *arguments, char *comment);
+static void CommentLargeObject(List *qualname, char *comment);
+static void CommentCast(List *qualname, List *arguments, char *comment);
/*
@@ -107,6 +113,21 @@ CommentObject(CommentStmt *stmt)
case OBJECT_CONSTRAINT:
CommentConstraint(stmt->objname, stmt->comment);
break;
+ case OBJECT_CONVERSION:
+ CommentConversion(stmt->objname, stmt->comment);
+ break;
+ case OBJECT_LANGUAGE:
+ CommentLanguage(stmt->objname, stmt->comment);
+ break;
+ case OBJECT_OPCLASS:
+ CommentOpClass(stmt->objname, stmt->objargs, stmt->comment);
+ break;
+ case OBJECT_LARGEOBJECT:
+ CommentLargeObject(stmt->objname, stmt->comment);
+ break;
+ case OBJECT_CAST:
+ CommentCast(stmt->objname, stmt->objargs, stmt->comment);
+ break;
default:
elog(ERROR, "unrecognized object type: %d",
(int) stmt->objtype);
@@ -592,7 +613,10 @@ CommentRule(List *qualname, char *comment)
PointerGetDatum(rulename),
0, 0);
if (!HeapTupleIsValid(tuple))
- elog(ERROR, "cache lookup failed for rule \"%s\"", rulename);
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("rule \"%s\" for relation \"%s\" does not exist",
+ rulename, RelationGetRelationName(relation))));
Assert(reloid == ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class);
ruleoid = HeapTupleGetOid(tuple);
ReleaseSysCache(tuple);
@@ -910,3 +934,297 @@ CommentConstraint(List *qualname, char *comment)
heap_close(pg_constraint, AccessShareLock);
heap_close(relation, NoLock);
}
+
+/*
+ * CommentConversion --
+ *
+ * This routine is used to add/drop any user-comments a user might
+ * have regarding a CONVERSION. The conversion is specified by name
+ * and, if found, and the user has appropriate permissions, a
+ * comment will be added/dropped using the CreateComments() routine.
+ * The conversion's name and the comment are the parameters to this routine.
+ */
+static void
+CommentConversion(List *qualname, char *comment)
+{
+ Oid conversionOid;
+ Oid classoid;
+
+ conversionOid = FindConversionByName(qualname);
+ if (!OidIsValid(conversionOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("conversion \"%s\" does not exist",
+ NameListToString(qualname))));
+
+ /* Check object security */
+ if (!pg_conversion_ownercheck(conversionOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
+ NameListToString(qualname));
+
+ /* pg_conversion doesn't have a hard-coded OID, so must look it up */
+ classoid = get_system_catalog_relid(ConversionRelationName);
+
+ /* Call CreateComments() to create/drop the comments */
+ CreateComments(conversionOid, classoid, 0, comment);
+}
+
+/*
+ * CommentLanguage --
+ *
+ * This routine is used to add/drop any user-comments a user might
+ * have regarding a LANGUAGE. The language is specified by name
+ * and, if found, and the user has appropriate permissions, a
+ * comment will be added/dropped using the CreateComments() routine.
+ * The language's name and the comment are the parameters to this routine.
+ */
+static void
+CommentLanguage(List *qualname, char *comment)
+{
+ Oid oid;
+ Oid classoid;
+ char *language;
+
+ if (length(qualname) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("language name may not be qualified")));
+ language = strVal(lfirst(qualname));
+
+ oid = GetSysCacheOid(LANGNAME,
+ CStringGetDatum(language),
+ 0, 0, 0);
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_SCHEMA),
+ errmsg("language \"%s\" does not exist", language)));
+
+ /* Check object security */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to comment on procedural language")));
+
+ /* pg_language doesn't have a hard-coded OID, so must look it up */
+ classoid = get_system_catalog_relid(LanguageRelationName);
+
+ /* Call CreateComments() to create/drop the comments */
+ CreateComments(oid, classoid, 0, comment);
+}
+
+/*
+ * CommentOpClass --
+ *
+ * This routine is used to allow a user to provide comments on an
+ * operator class. The operator class for commenting is determined by both
+ * its name and its argument list which defines the index method
+ * the operator class is used for. The argument list is expected to contain
+ * a single name (represented as a string Value node).
+ */
+static void
+CommentOpClass(List *qualname, List *arguments, char *comment)
+{
+ char *amname;
+ char *schemaname;
+ char *opcname;
+ Oid amID;
+ Oid opcID;
+ Oid classoid;
+ HeapTuple tuple;
+
+ Assert(length(arguments) == 1);
+ amname = strVal(lfirst(arguments));
+
+ /*
+ * Get the access method's OID.
+ */
+ amID = GetSysCacheOid(AMNAME,
+ CStringGetDatum(amname),
+ 0, 0, 0);
+ if (!OidIsValid(amID))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("access method \"%s\" does not exist",
+ amname)));
+
+ /*
+ * Look up the opclass.
+ */
+
+ /* deconstruct the name list */
+ DeconstructQualifiedName(qualname, &schemaname, &opcname);
+
+ if (schemaname)
+ {
+ /* Look in specific schema only */
+ Oid namespaceId;
+
+ namespaceId = LookupExplicitNamespace(schemaname);
+ tuple = SearchSysCache(CLAAMNAMENSP,
+ ObjectIdGetDatum(amID),
+ PointerGetDatum(opcname),
+ ObjectIdGetDatum(namespaceId),
+ 0);
+ }
+ else
+ {
+ /* Unqualified opclass name, so search the search path */
+ opcID = OpclassnameGetOpcid(amID, opcname);
+ if (!OidIsValid(opcID))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator class \"%s\" does not exist for access method \"%s\"",
+ opcname, amname)));
+ tuple = SearchSysCache(CLAOID,
+ ObjectIdGetDatum(opcID),
+ 0, 0, 0);
+ }
+
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator class \"%s\" does not exist for access method \"%s\"",
+ NameListToString(qualname), amname)));
+
+ opcID = HeapTupleGetOid(tuple);
+
+ /* Permission check: must own opclass */
+ if (!pg_opclass_ownercheck(opcID, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
+ NameListToString(qualname));
+
+ ReleaseSysCache(tuple);
+
+ /* pg_opclass doesn't have a hard-coded OID, so must look it up */
+ classoid = get_system_catalog_relid(OperatorClassRelationName);
+
+ /* Call CreateComments() to create/drop the comments */
+ CreateComments(opcID, classoid, 0, comment);
+}
+
+/*
+ * CommentLargeObject --
+ *
+ * This routine is used to add/drop any user-comments a user might
+ * have regarding a LARGE OBJECT. The large object is specified by OID
+ * and, if found, and the user has appropriate permissions, a
+ * comment will be added/dropped using the CreateComments() routine.
+ * The large object's OID and the comment are the parameters to this routine.
+ */
+static void
+CommentLargeObject(List *qualname, char *comment)
+{
+ Oid loid;
+ Oid classoid;
+ Node *node;
+
+ Assert(length(qualname) == 1);
+ node = (Node *) lfirst(qualname);
+
+ switch (nodeTag(node))
+ {
+ case T_Integer:
+ loid = intVal(node);
+ break;
+ case T_Float:
+ /*
+ * Values too large for int4 will be represented as Float
+ * constants by the lexer. Accept these if they are valid
+ * OID strings.
+ */
+ loid = DatumGetObjectId(DirectFunctionCall1(oidin,
+ CStringGetDatum(strVal(node))));
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(node));
+ /* keep compiler quiet */
+ loid = InvalidOid;
+ }
+
+ /* check that the large object exists */
+ if (!LargeObjectExists(loid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("large object %u does not exist", loid)));
+
+ /* pg_largeobject doesn't have a hard-coded OID, so must look it up */
+ classoid = get_system_catalog_relid(LargeObjectRelationName);
+
+ /* Call CreateComments() to create/drop the comments */
+ CreateComments(loid, classoid, 0, comment);
+}
+
+/*
+ * CommentCast --
+ *
+ * This routine is used to add/drop any user-comments a user might
+ * have regarding a CAST. The cast is specified by source and destination types
+ * and, if found, and the user has appropriate permissions, a
+ * comment will be added/dropped using the CreateComments() routine.
+ * The cast's source type is passed as the "name", the destination type
+ * as the "arguments".
+ */
+static void
+CommentCast(List *qualname, List *arguments, char *comment)
+{
+ TypeName *sourcetype;
+ TypeName *targettype;
+ Oid sourcetypeid;
+ Oid targettypeid;
+ HeapTuple tuple;
+ Oid castOid;
+ Oid classoid;
+
+ Assert(length(qualname) == 1);
+ sourcetype = (TypeName *) lfirst(qualname);
+ Assert(IsA(sourcetype, TypeName));
+ Assert(length(arguments) == 1);
+ targettype = (TypeName *) lfirst(arguments);
+ Assert(IsA(targettype, TypeName));
+
+ sourcetypeid = typenameTypeId(sourcetype);
+ if (!OidIsValid(sourcetypeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("source data type %s does not exist",
+ TypeNameToString(sourcetype))));
+
+ targettypeid = typenameTypeId(targettype);
+ if (!OidIsValid(targettypeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("target data type %s does not exist",
+ TypeNameToString(targettype))));
+
+ tuple = SearchSysCache(CASTSOURCETARGET,
+ ObjectIdGetDatum(sourcetypeid),
+ ObjectIdGetDatum(targettypeid),
+ 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("cast from type %s to type %s does not exist",
+ TypeNameToString(sourcetype),
+ TypeNameToString(targettype))));
+
+ /* Get the OID of the cast */
+ castOid = HeapTupleGetOid(tuple);
+
+ /* Permission check */
+ if (!pg_type_ownercheck(sourcetypeid, GetUserId())
+ && !pg_type_ownercheck(targettypeid, GetUserId()))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be owner of type %s or type %s",
+ TypeNameToString(sourcetype),
+ TypeNameToString(targettype))));
+
+ ReleaseSysCache(tuple);
+
+ /* pg_cast doesn't have a hard-coded OID, so must look it up */
+ classoid = get_system_catalog_relid(CastRelationName);
+
+ /* Call CreateComments() to create/drop the comments */
+ CreateComments(castOid, classoid, 0, comment);
+}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 7e3c7410a1..417220df9d 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -2,14 +2,15 @@
*
* functioncmds.c
*
- * Routines for CREATE and DROP FUNCTION commands
+ * Routines for CREATE and DROP FUNCTION commands and CREATE and DROP
+ * CAST commands.
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.40 2003/11/12 21:15:50 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.41 2003/11/21 22:32:48 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5f0b0f4e5d..337eef4480 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.437 2003/11/06 22:08:14 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.438 2003/11/21 22:32:49 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -363,7 +363,7 @@ static void doNegateFloat(Value *v);
KEY
- LANCOMPILER LANGUAGE LAST_P LEADING LEFT LEVEL LIKE LIMIT
+ LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEFT LEVEL LIKE LIMIT
LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
LOCK_P
@@ -373,7 +373,7 @@ static void doNegateFloat(Value *v);
NOCREATEUSER NONE NOT NOTHING NOTIFY NOTNULL NULL_P
NULLIF NUMERIC
- OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR
+ OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR
ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNER
PARTIAL PASSWORD PATH_P PENDANT PLACING POSITION
@@ -2519,11 +2519,15 @@ TruncateStmt:
* The COMMENT ON statement can take different forms based upon the type of
* the object associated with the comment. The form of the statement is:
*
- * COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW ]
- * | AGGREGATE () | FUNCTION
- * (arg1, arg2, ...) | OPERATOR
- * (leftoperand_typ rightoperand_typ) | TRIGGER ON
- * | RULE ON ] IS 'text'
+ * COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
+ * CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
+ * CAST ] |
+ * AGGREGATE () |
+ * FUNCTION (arg1, arg2, ...) |
+ * OPERATOR (leftoperand_typ, rightoperand_typ) |
+ * TRIGGER ON |
+ * RULE ON ]
+ * IS 'text'
*
*****************************************************************************/
@@ -2603,6 +2607,42 @@ CommentStmt:
n->comment = $8;
$$ = (Node *) n;
}
+ | COMMENT ON OPERATOR CLASS any_name USING access_method IS comment_text
+ {
+ CommentStmt *n = makeNode(CommentStmt);
+ n->objtype = OBJECT_OPCLASS;
+ n->objname = $5;
+ n->objargs = makeList1(makeString($7));
+ n->comment = $9;
+ $$ = (Node *) n;
+ }
+ | COMMENT ON LARGE_P OBJECT_P NumericOnly IS comment_text
+ {
+ CommentStmt *n = makeNode(CommentStmt);
+ n->objtype = OBJECT_LARGEOBJECT;
+ n->objname = makeList1($5);
+ n->objargs = NIL;
+ n->comment = $7;
+ $$ = (Node *) n;
+ }
+ | COMMENT ON CAST '(' Typename AS Typename ')' IS comment_text
+ {
+ CommentStmt *n = makeNode(CommentStmt);
+ n->objtype = OBJECT_CAST;
+ n->objname = makeList1($5);
+ n->objargs = makeList1($7);
+ n->comment = $10;
+ $$ = (Node *) n;
+ }
+ | COMMENT ON opt_procedural LANGUAGE any_name IS comment_text
+ {
+ CommentStmt *n = makeNode(CommentStmt);
+ n->objtype = OBJECT_LANGUAGE;
+ n->objname = $5;
+ n->objargs = NIL;
+ n->comment = $7;
+ $$ = (Node *) n;
+ }
;
comment_type:
@@ -2615,6 +2655,7 @@ comment_type:
| DOMAIN_P { $$ = OBJECT_TYPE; }
| TYPE_P { $$ = OBJECT_TYPE; }
| VIEW { $$ = OBJECT_VIEW; }
+ | CONVERSION_P { $$ = OBJECT_CONVERSION; }
;
comment_text:
@@ -7365,6 +7406,7 @@ unreserved_keyword:
| KEY
| LANCOMPILER
| LANGUAGE
+ | LARGE_P
| LAST_P
| LEVEL
| LISTEN
@@ -7387,6 +7429,7 @@ unreserved_keyword:
| NOCREATEUSER
| NOTHING
| NOTIFY
+ | OBJECT_P
| OF
| OIDS
| OPERATOR
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 6d42a13ef8..c4e5058078 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.142 2003/11/06 22:08:15 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.143 2003/11/21 22:32:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -177,6 +177,7 @@ static const ScanKeyword ScanKeywords[] = {
{"key", KEY},
{"lancompiler", LANCOMPILER},
{"language", LANGUAGE},
+ {"large", LARGE_P},
{"last", LAST_P},
{"leading", LEADING},
{"left", LEFT},
@@ -214,6 +215,7 @@ static const ScanKeyword ScanKeywords[] = {
{"null", NULL_P},
{"nullif", NULLIF},
{"numeric", NUMERIC},
+ {"object", OBJECT_P},
{"of", OF},
{"off", OFF},
{"offset", OFFSET},
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index f777fb33b9..65a4602806 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/large_object/inv_api.c,v 1.100 2003/11/12 21:15:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/large_object/inv_api.c,v 1.101 2003/11/21 22:32:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -31,12 +31,14 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_type.h"
+#include "commands/comment.h"
#include "libpq/libpq-fs.h"
#include "miscadmin.h"
#include "storage/large_object.h"
#include "storage/smgr.h"
-#include "utils/fmgroids.h"
#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
static int32
@@ -174,8 +176,16 @@ inv_close(LargeObjectDesc *obj_desc)
int
inv_drop(Oid lobjId)
{
+ Oid classoid;
+
LargeObjectDrop(lobjId);
+ /* pg_largeobject doesn't have a hard-coded OID, so must look it up */
+ classoid = get_system_catalog_relid(LargeObjectRelationName);
+
+ /* Delete any comments on the large object */
+ DeleteComments(lobjId, classoid, 0);
+
/*
* Advance command counter so that tuple removal will be seen by later
* large-object operations in this transaction.
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 98b43f5cce..fb4979b453 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.75 2003/08/04 02:40:09 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.76 2003/11/21 22:32:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -61,6 +61,7 @@ dumpSchema(Archive *fout,
int numAggregates;
int numOperators;
int numOpclasses;
+ int numConversions;
NamespaceInfo *nsinfo;
TypeInfo *tinfo;
FuncInfo *finfo;
@@ -69,6 +70,7 @@ dumpSchema(Archive *fout,
InhInfo *inhinfo;
OprInfo *oprinfo;
OpclassInfo *opcinfo;
+ ConvInfo *convinfo;
if (g_verbose)
write_msg(NULL, "reading schemas\n");
@@ -94,6 +96,10 @@ dumpSchema(Archive *fout,
write_msg(NULL, "reading user-defined operator classes\n");
opcinfo = getOpclasses(&numOpclasses);
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined conversions\n");
+ convinfo = getConversions(&numConversions);
+
if (g_verbose)
write_msg(NULL, "reading user-defined tables\n");
tblinfo = getTables(&numTables);
@@ -190,6 +196,13 @@ dumpSchema(Archive *fout,
dumpCasts(fout, finfo, numFuncs, tinfo, numTypes);
}
+ if (!dataOnly)
+ {
+ if (g_verbose)
+ write_msg(NULL, "dumping out user-defined conversions\n");
+ dumpConversions(fout, convinfo, numConversions);
+ }
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 134e9522a3..f74cd30b5a 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12,7 +12,7 @@
* by PostgreSQL
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.355 2003/10/28 21:05:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.356 2003/11/21 22:32:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -105,6 +105,7 @@ static const char *convertRegProcReference(const char *proc);
static const char *convertOperatorReference(const char *opr,
OprInfo *g_oprinfo, int numOperators);
static void dumpOneOpclass(Archive *fout, OpclassInfo *opcinfo);
+static void dumpOneConversion(Archive *fout, ConvInfo *convinfo);
static void dumpOneAgg(Archive *fout, AggInfo *agginfo);
static Oid findLastBuiltinOid_V71(const char *);
static Oid findLastBuiltinOid_V70(void);
@@ -1689,6 +1690,79 @@ getOperators(int *numOprs)
return oprinfo;
}
+/*
+ * getConversions:
+ * read all conversions in the system catalogs and return them in the
+ * ConvInfo* structure
+ *
+ * numConversions is set to the number of conversions read in
+ */
+ConvInfo *
+getConversions(int *numConversions)
+{
+ PGresult *res;
+ int ntups;
+ int i;
+ PQExpBuffer query = createPQExpBuffer();
+ ConvInfo *convinfo;
+ int i_oid;
+ int i_conname;
+ int i_connamespace;
+ int i_usename;
+
+ /* Conversions didn't exist pre-7.3 */
+ if (g_fout->remoteVersion < 70300) {
+ *numConversions = 0;
+ return NULL;
+ }
+
+ /*
+ * find all conversions, including builtin conversions; we filter out
+ * system-defined conversions at dump-out time.
+ */
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT pg_conversion.oid, conname, "
+ "connamespace, "
+ "(select usename from pg_user where conowner = usesysid) as usename "
+ "from pg_conversion");
+
+ res = PQexec(g_conn, query->data);
+ if (!res ||
+ PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ write_msg(NULL, "query to obtain list of conversions failed: %s", PQerrorMessage(g_conn));
+ exit_nicely();
+ }
+
+ ntups = PQntuples(res);
+ *numConversions = ntups;
+
+ convinfo = (ConvInfo *) malloc(ntups * sizeof(ConvInfo));
+
+ i_oid = PQfnumber(res, "oid");
+ i_conname = PQfnumber(res, "conname");
+ i_connamespace = PQfnumber(res, "connamespace");
+ i_usename = PQfnumber(res, "usename");
+
+ for (i = 0; i < ntups; i++)
+ {
+ convinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
+ convinfo[i].conname = strdup(PQgetvalue(res, i, i_conname));
+ convinfo[i].connamespace = findNamespace(PQgetvalue(res, i, i_connamespace),
+ convinfo[i].oid);
+ convinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return convinfo;
+}
+
/*
* getOpclasses:
* read all opclasses in the system catalogs and return them in the
@@ -3414,6 +3488,7 @@ dumpOneCompositeType(Archive *fout, TypeInfo *tinfo)
tinfo->usename, "TYPE", NULL,
q->data, delq->data, NULL, NULL, NULL);
+
/* Dump Type Comments */
resetPQExpBuffer(q);
@@ -3614,6 +3689,14 @@ dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs)
NULL, lanacl, lanoid);
free(tmp);
}
+
+ /* Dump Proc Lang Comments */
+ resetPQExpBuffer(defqry);
+
+ appendPQExpBuffer(defqry, "LANGUAGE %s", fmtId(lanname));
+ dumpComment(fout, defqry->data,
+ NULL, "",
+ lanoid, "pg_language", 0, NULL);
}
PQclear(res);
@@ -4019,6 +4102,16 @@ dumpCasts(Archive *fout,
"CAST", deps,
defqry->data, delqry->data,
NULL, NULL, NULL);
+
+ /* Dump Cast Comments */
+ resetPQExpBuffer(defqry);
+ appendPQExpBuffer(defqry, "CAST (%s AS %s)",
+ getFormattedTypeName(castsource, zeroAsNone),
+ getFormattedTypeName(casttarget, zeroAsNone));
+ dumpComment(fout, defqry->data,
+ NULL, "",
+ castoid, "pg_cast", 0, NULL);
+
}
PQclear(res);
@@ -4490,7 +4583,8 @@ dumpOneOpclass(Archive *fout, OpclassInfo *opcinfo)
opcintype = PQgetvalue(res, 0, i_opcintype);
opckeytype = PQgetvalue(res, 0, i_opckeytype);
opcdefault = PQgetvalue(res, 0, i_opcdefault);
- amname = PQgetvalue(res, 0, i_amname);
+ /* amname will still be needed after we PQclear res */
+ amname = strdup(PQgetvalue(res, 0, i_amname));
/*
* DROP must be fully qualified in case same name appears in
@@ -4617,11 +4711,145 @@ dumpOneOpclass(Archive *fout, OpclassInfo *opcinfo)
q->data, delq->data,
NULL, NULL, NULL);
+ /* Dump Operator Class Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "OPERATOR CLASS %s",
+ fmtId(opcinfo->opcname));
+ appendPQExpBuffer(q, " USING %s",
+ fmtId(amname));
+ dumpComment(fout, q->data,
+ NULL, opcinfo->usename,
+ opcinfo->oid, "pg_opclass", 0, NULL);
+
+ free(amname);
destroyPQExpBuffer(query);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
}
+/*
+ * dumpConversions
+ * writes out to fout the queries to create all the user-defined conversions
+ */
+void
+dumpConversions(Archive *fout, ConvInfo convinfo[], int numConvs)
+{
+ int i;
+
+ for (i = 0; i < numConvs; i++)
+ {
+ /* Dump only conversions in dumpable namespaces */
+ if (!convinfo[i].connamespace->dump)
+ continue;
+
+ dumpOneConversion(fout, &convinfo[i]);
+ }
+}
+
+/*
+ * dumpOneConversion
+ * write out a single conversion definition
+ */
+static void
+dumpOneConversion(Archive *fout, ConvInfo *convinfo)
+{
+ PQExpBuffer query = createPQExpBuffer();
+ PQExpBuffer q = createPQExpBuffer();
+ PQExpBuffer delq = createPQExpBuffer();
+ PQExpBuffer details = createPQExpBuffer();
+ PGresult *res;
+ int ntups;
+ int i_conname;
+ int i_conforencoding;
+ int i_contoencoding;
+ int i_conproc;
+ int i_condefault;
+ const char *conname;
+ const char *conforencoding;
+ const char *contoencoding;
+ const char *conproc;
+ bool condefault;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(convinfo->connamespace->nspname);
+
+ /* Get conversion-specific details */
+ appendPQExpBuffer(query, "SELECT conname,
+ pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding,
+ pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding,
+ conproc, condefault
+ FROM pg_catalog.pg_conversion c
+ WHERE c.oid = '%s'::pg_catalog.oid",
+ convinfo->oid);
+
+ res = PQexec(g_conn, query->data);
+ if (!res ||
+ PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ write_msg(NULL, "query to obtain conversion failed: %s",
+ PQerrorMessage(g_conn));
+ exit_nicely();
+ }
+
+ /* Expecting a single result only */
+ ntups = PQntuples(res);
+ if (ntups != 1)
+ {
+ write_msg(NULL, "Got %d rows instead of one from: %s",
+ ntups, query->data);
+ exit_nicely();
+ }
+
+ i_conname = PQfnumber(res, "conname");
+ i_conforencoding = PQfnumber(res, "conforencoding");
+ i_contoencoding = PQfnumber(res, "contoencoding");
+ i_conproc = PQfnumber(res, "conproc");
+ i_condefault = PQfnumber(res, "condefault");
+
+ conname = PQgetvalue(res, 0, i_conname);
+ conforencoding = PQgetvalue(res, 0, i_conforencoding);
+ contoencoding = PQgetvalue(res, 0, i_contoencoding);
+ conproc = PQgetvalue(res, 0, i_conproc);
+ condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
+
+ /*
+ * DROP must be fully qualified in case same name appears in
+ * pg_catalog
+ */
+ appendPQExpBuffer(delq, "DROP CONVERSION %s",
+ fmtId(convinfo->connamespace->nspname));
+ appendPQExpBuffer(delq, ".%s;\n",
+ fmtId(convinfo->conname));
+
+ appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
+ (condefault) ? "DEFAULT " : "",
+ fmtId(convinfo->conname));
+ appendStringLiteral(q, conforencoding, true);
+ appendPQExpBuffer(q, " TO ");
+ appendStringLiteral(q, contoencoding, true);
+ /* regproc is automatically quoted in 7.3 and above */
+ appendPQExpBuffer(q, " FROM %s;\n", conproc);
+
+ ArchiveEntry(fout, convinfo->oid, convinfo->conname,
+ convinfo->connamespace->nspname, convinfo->usename,
+ "CONVERSION", NULL,
+ q->data, delq->data,
+ NULL, NULL, NULL);
+
+ /* Dump Conversion Comments */
+ resetPQExpBuffer(q);
+ appendPQExpBuffer(q, "CONVERSION %s", fmtId(convinfo->conname));
+ dumpComment(fout, q->data,
+ convinfo->connamespace->nspname, convinfo->usename,
+ convinfo->oid, "pg_conversion", 0, NULL);
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(details);
+}
/*
* dumpAggs
@@ -6573,8 +6801,10 @@ dumpRules(Archive *fout, TableInfo *tblinfo, int numTables)
/* Dump rule comments */
resetPQExpBuffer(query);
- appendPQExpBuffer(query, "RULE %s", fmtId(PQgetvalue(res, i, i_rulename)));
- appendPQExpBuffer(query, " ON %s", fmtId(tbinfo->relname));
+ appendPQExpBuffer(query, "RULE %s",
+ fmtId(PQgetvalue(res, i, i_rulename)));
+ appendPQExpBuffer(query, " ON %s",
+ fmtId(tbinfo->relname));
dumpComment(fout, query->data,
tbinfo->relnamespace->nspname,
tbinfo->usename,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index a68a99ade0..1e6e2fbe11 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_dump.h,v 1.104 2003/08/08 04:52:21 momjian Exp $
+ * $Id: pg_dump.h,v 1.105 2003/11/21 22:32:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -95,6 +95,14 @@ typedef struct _opclassInfo
char *usename;
} OpclassInfo;
+typedef struct _convInfo
+{
+ char *oid;
+ char *conname;
+ NamespaceInfo *connamespace; /* link to containing namespace */
+ char *usename;
+} ConvInfo;
+
typedef struct _tableInfo
{
/*
@@ -213,6 +221,7 @@ extern FuncInfo *getFuncs(int *numFuncs);
extern AggInfo *getAggregates(int *numAggregates);
extern OprInfo *getOperators(int *numOperators);
extern OpclassInfo *getOpclasses(int *numOpclasses);
+extern ConvInfo *getConversions(int *numConversions);
extern TableInfo *getTables(int *numTables);
extern InhInfo *getInherits(int *numInherits);
@@ -230,6 +239,8 @@ extern void dumpAggs(Archive *fout, AggInfo agginfo[], int numAggregates);
extern void dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators);
extern void dumpOpclasses(Archive *fout,
OpclassInfo *opcinfo, int numOpclasses);
+extern void dumpConversions(Archive *fout,
+ ConvInfo *coninfo, int numConversions);
extern void dumpTables(Archive *fout, TableInfo tblinfo[], int numTables,
const bool aclsSkip,
const bool schemaOnly, const bool dataOnly);
diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c
index 53235b423a..16cb1b947a 100644
--- a/src/bin/psql/large_obj.c
+++ b/src/bin/psql/large_obj.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
- * $Header: /cvsroot/pgsql/src/bin/psql/large_obj.c,v 1.29 2003/08/04 23:59:40 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/large_obj.c,v 1.30 2003/11/21 22:32:49 tgl Exp $
*/
#include "postgres_fe.h"
#include "large_obj.h"
@@ -165,9 +165,7 @@ do_lo_import(const char *filename_arg, const char *comment_arg)
}
/* insert description if given */
- /* XXX don't try to hack pg_description if not superuser */
- /* XXX ought to replace this with some kind of COMMENT command */
- if (comment_arg && is_superuser())
+ if (comment_arg)
{
char *cmdbuf;
char *bufptr;
@@ -177,9 +175,7 @@ do_lo_import(const char *filename_arg, const char *comment_arg)
if (!cmdbuf)
return fail_lo_xact("\\lo_import", own_transaction);
sprintf(cmdbuf,
- "INSERT INTO pg_catalog.pg_description VALUES ('%u', "
- "'pg_catalog.pg_largeobject'::regclass, "
- "0, '",
+ "COMMENT ON LARGE OBJECT %u IS '",
loid);
bufptr = cmdbuf + strlen(cmdbuf);
for (i = 0; i < slen; i++)
@@ -188,7 +184,7 @@ do_lo_import(const char *filename_arg, const char *comment_arg)
*bufptr++ = '\\';
*bufptr++ = comment_arg[i];
}
- strcpy(bufptr, "')");
+ strcpy(bufptr, "'");
if (!(res = PSQLexec(cmdbuf, false)))
{
@@ -219,10 +215,8 @@ do_lo_import(const char *filename_arg, const char *comment_arg)
bool
do_lo_unlink(const char *loid_arg)
{
- PGresult *res;
int status;
Oid loid = atooid(loid_arg);
- char buf[256];
bool own_transaction;
if (!start_lo_xact("\\lo_unlink", &own_transaction))
@@ -235,20 +229,6 @@ do_lo_unlink(const char *loid_arg)
return fail_lo_xact("\\lo_unlink", own_transaction);
}
- /* remove the comment as well */
- /* XXX don't try to hack pg_description if not superuser */
- /* XXX ought to replace this with some kind of COMMENT command */
- if (is_superuser())
- {
- snprintf(buf, sizeof(buf),
- "DELETE FROM pg_catalog.pg_description WHERE objoid = '%u' "
- "AND classoid = 'pg_catalog.pg_largeobject'::regclass",
- loid);
- if (!(res = PSQLexec(buf, false)))
- return fail_lo_xact("\\lo_unlink", own_transaction);
- PQclear(res);
- }
-
if (!finish_lo_xact("\\lo_unlink", own_transaction))
return false;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2aec3b7b7a..e0fada5dbe 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parsenodes.h,v 1.248 2003/09/17 04:25:29 ishii Exp $
+ * $Id: parsenodes.h,v 1.249 2003/11/21 22:32:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -686,6 +686,7 @@ typedef enum ObjectType
OBJECT_GROUP,
OBJECT_INDEX,
OBJECT_LANGUAGE,
+ OBJECT_LARGEOBJECT,
OBJECT_OPCLASS,
OBJECT_OPERATOR,
OBJECT_RULE,
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 8a4c235cea..53fb98df93 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: acl.h,v 1.63 2003/10/29 22:20:54 tgl Exp $
+ * $Id: acl.h,v 1.64 2003/11/21 22:32:49 tgl Exp $
*
* NOTES
* An ACL array is simply an array of AclItems, representing the union
@@ -245,5 +245,6 @@ extern bool pg_proc_ownercheck(Oid proc_oid, AclId userid);
extern bool pg_namespace_ownercheck(Oid nsp_oid, AclId userid);
extern bool pg_opclass_ownercheck(Oid opc_oid, AclId userid);
extern bool pg_database_ownercheck(Oid db_oid, AclId userid);
+extern bool pg_conversion_ownercheck(Oid conv_oid, AclId userid);
#endif /* ACL_H */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 61a3965fe5..cc50cb86da 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -3,6 +3,10 @@
-- add attribute
--
CREATE TABLE tmp (initial int4);
+COMMENT ON TABLE tmp_wrong IS 'table comment';
+ERROR: relation "tmp_wrong" does not exist
+COMMENT ON TABLE tmp IS 'table comment';
+COMMENT ON TABLE tmp IS NULL;
ALTER TABLE tmp ADD COLUMN a int4;
ALTER TABLE tmp ADD COLUMN b name;
ALTER TABLE tmp ADD COLUMN c text;
diff --git a/src/test/regress/expected/conversion.out b/src/test/regress/expected/conversion.out
index 289bd0a27f..c0e85b85a1 100644
--- a/src/test/regress/expected/conversion.out
+++ b/src/test/regress/expected/conversion.out
@@ -18,6 +18,11 @@ CREATE DEFAULT CONVERSION public.mydef FOR 'LATIN1' TO 'UNICODE' FROM iso8859_1_
--
CREATE DEFAULT CONVERSION public.mydef2 FOR 'LATIN1' TO 'UNICODE' FROM iso8859_1_to_utf8;
ERROR: default conversion for LATIN1 to UNICODE already exists
+-- test comments
+COMMENT ON CONVERSION myconv_bad IS 'foo';
+ERROR: conversion "myconv_bad" does not exist
+COMMENT ON CONVERSION myconv IS 'bar';
+COMMENT ON CONVERSION myconv IS NULL;
--
-- drop user defined conversion
--
diff --git a/src/test/regress/expected/create_aggregate.out b/src/test/regress/expected/create_aggregate.out
index ef83c886a1..b0fec460cb 100644
--- a/src/test/regress/expected/create_aggregate.out
+++ b/src/test/regress/expected/create_aggregate.out
@@ -7,6 +7,11 @@ CREATE AGGREGATE newavg (
finalfunc = numeric_avg,
initcond1 = '{0,0,0}'
);
+-- test comments
+COMMENT ON AGGREGATE newavg_wrong (int4) IS 'an agg comment';
+ERROR: aggregate newavg_wrong(integer) does not exist
+COMMENT ON AGGREGATE newavg (int4) IS 'an agg comment';
+COMMENT ON AGGREGATE newavg (int4) IS NULL;
-- without finalfunc; test obsolete spellings 'sfunc1' etc
CREATE AGGREGATE newsum (
sfunc1 = int4pl, basetype = int4, stype1 = int4,
@@ -17,3 +22,7 @@ CREATE AGGREGATE newcnt (
sfunc = int4inc, basetype = 'any', stype = int4,
initcond = '0'
);
+COMMENT ON AGGREGATE nosuchagg (*) IS 'should fail';
+ERROR: aggregate nosuchagg(*) does not exist
+COMMENT ON AGGREGATE newcnt (*) IS 'an any agg comment';
+COMMENT ON AGGREGATE newcnt (*) IS NULL;
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 46d2657d01..6643070e71 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -18,6 +18,11 @@ CREATE INDEX tenk2_hundred ON tenk2 USING btree(hundred int4_ops);
CREATE INDEX rix ON road USING btree (name text_ops);
CREATE INDEX iix ON ihighway USING btree (name text_ops);
CREATE INDEX six ON shighway USING btree (name text_ops);
+-- test comments
+COMMENT ON INDEX six_wrong IS 'bad index';
+ERROR: relation "six_wrong" does not exist
+COMMENT ON INDEX six IS 'good index';
+COMMENT ON INDEX six IS NULL;
--
-- BTREE ascending/descending cases
--
diff --git a/src/test/regress/expected/create_operator.out b/src/test/regress/expected/create_operator.out
index 2338b7c710..7685dbe802 100644
--- a/src/test/regress/expected/create_operator.out
+++ b/src/test/regress/expected/create_operator.out
@@ -26,3 +26,8 @@ CREATE OPERATOR #%# (
leftarg = int4, -- right unary
procedure = int4fac
);
+-- Test comments
+COMMENT ON OPERATOR ###### (int4, NONE) IS 'bad right unary';
+ERROR: operator does not exist: integer ######
+COMMENT ON OPERATOR #%# (int4, NONE) IS 'right unary';
+COMMENT ON OPERATOR #%# (int4, NONE) IS NULL;
diff --git a/src/test/regress/expected/create_type.out b/src/test/regress/expected/create_type.out
index 210da92cf2..101382e739 100644
--- a/src/test/regress/expected/create_type.out
+++ b/src/test/regress/expected/create_type.out
@@ -71,6 +71,11 @@ SELECT * FROM get_default_test();
zippo | 42
(1 row)
+-- Test comments
+COMMENT ON TYPE bad IS 'bad comment';
+ERROR: type "bad" does not exist
+COMMENT ON TYPE default_test_row IS 'good comment';
+COMMENT ON TYPE default_test_row IS NULL;
DROP TYPE default_test_row CASCADE;
NOTICE: drop cascades to function get_default_test()
DROP TABLE default_test;
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index 630855ed37..04e62f7084 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -15,6 +15,11 @@ CREATE VIEW iexit AS
CREATE VIEW toyemp AS
SELECT name, age, location, 12*salary AS annualsal
FROM emp;
+-- Test comments
+COMMENT ON VIEW noview IS 'no view';
+ERROR: relation "noview" does not exist
+COMMENT ON VIEW toyemp IS 'is a view';
+COMMENT ON VIEW toyemp IS NULL;
--
-- CREATE OR REPLACE VIEW
--
diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out
index bfb90979f6..57a4ae1647 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -64,6 +64,11 @@ CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 text, PRIMARY KEY(ptest1,
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, CONSTRAINT constrname FOREIGN KEY(ftest1, ftest2)
REFERENCES PKTABLE MATCH FULL ON DELETE SET NULL ON UPDATE SET NULL);
+-- Test comments
+COMMENT ON CONSTRAINT constrname_wrong ON FKTABLE IS 'fk constraint comment';
+ERROR: constraint "constrname_wrong" for table "fktable" does not exist
+COMMENT ON CONSTRAINT constrname ON FKTABLE IS 'fk constraint comment';
+COMMENT ON CONSTRAINT constrname ON FKTABLE IS NULL;
-- Insert test data into PKTABLE
INSERT INTO PKTABLE VALUES (1, 2, 'Test1');
INSERT INTO PKTABLE VALUES (1, 3, 'Test1-2');
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index a7a380b5c6..694165e506 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -264,6 +264,11 @@ begin
return 0;
end;
' language 'plpgsql';
+-- Test comments
+COMMENT ON FUNCTION tg_hub_adjustslots_wrong(bpchar, integer, integer) IS 'function with args';
+ERROR: function tg_hub_adjustslots_wrong(character, integer, integer) does not exist
+COMMENT ON FUNCTION tg_hub_adjustslots(bpchar, integer, integer) IS 'function with args';
+COMMENT ON FUNCTION tg_hub_adjustslots(bpchar, integer, integer) IS NULL;
-- ************************************************************
-- * BEFORE INSERT or UPDATE on HSlot
-- * - prevent from manual manipulation
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 7ae49e8b44..ae7d0feea6 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -17,6 +17,11 @@ create rule rtest_v1_upd as on update to rtest_v1 do instead
where a = old.a;
create rule rtest_v1_del as on delete to rtest_v1 do instead
delete from rtest_t1 where a = old.a;
+-- Test comments
+COMMENT ON RULE rtest_v1_bad ON rtest_v1 IS 'bad rule';
+ERROR: rule "rtest_v1_bad" for relation "rtest_v1" does not exist
+COMMENT ON RULE rtest_v1_del ON rtest_v1 IS 'delete rule';
+COMMENT ON RULE rtest_v1_del ON rtest_v1 IS NULL;
--
-- Tables and rules for the constraint update/delete test
--
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 49b783a805..d09e5db170 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -71,3 +71,8 @@ SELECT nextval('sequence_test2');
5
(1 row)
+-- Test comments
+COMMENT ON SEQUENCE asdf IS 'won''t work';
+ERROR: relation "asdf" does not exist
+COMMENT ON SEQUENCE sequence_test2 IS 'will work';
+COMMENT ON SEQUENCE sequence_test2 IS NULL;
diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out
index 086c9f602c..c44a81a75f 100644
--- a/src/test/regress/expected/triggers.out
+++ b/src/test/regress/expected/triggers.out
@@ -37,6 +37,11 @@ create trigger check_fkeys2_pkey_exist
for each row
execute procedure
check_primary_key ('fkey21', 'fkey22', 'pkeys', 'pkey1', 'pkey2');
+-- Test comments
+COMMENT ON TRIGGER check_fkeys2_pkey_bad ON fkeys2 IS 'wrong';
+ERROR: trigger "check_fkeys2_pkey_bad" for table "fkeys2" does not exist
+COMMENT ON TRIGGER check_fkeys2_pkey_exist ON fkeys2 IS 'right';
+COMMENT ON TRIGGER check_fkeys2_pkey_exist ON fkeys2 IS NULL;
--
-- For pkeys:
-- ON DELETE/UPDATE (pkey1, pkey2) CASCADE:
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 9bdc8cd1ea..33187c8352 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -5,6 +5,10 @@
CREATE TABLE tmp (initial int4);
+COMMENT ON TABLE tmp_wrong IS 'table comment';
+COMMENT ON TABLE tmp IS 'table comment';
+COMMENT ON TABLE tmp IS NULL;
+
ALTER TABLE tmp ADD COLUMN a int4;
ALTER TABLE tmp ADD COLUMN b name;
diff --git a/src/test/regress/sql/conversion.sql b/src/test/regress/sql/conversion.sql
index 979a7cc0fd..c84ffee95f 100644
--- a/src/test/regress/sql/conversion.sql
+++ b/src/test/regress/sql/conversion.sql
@@ -16,6 +16,10 @@ CREATE DEFAULT CONVERSION public.mydef FOR 'LATIN1' TO 'UNICODE' FROM iso8859_1_
-- cannot make default conversion with same shcema/for_encoding/to_encoding
--
CREATE DEFAULT CONVERSION public.mydef2 FOR 'LATIN1' TO 'UNICODE' FROM iso8859_1_to_utf8;
+-- test comments
+COMMENT ON CONVERSION myconv_bad IS 'foo';
+COMMENT ON CONVERSION myconv IS 'bar';
+COMMENT ON CONVERSION myconv IS NULL;
--
-- drop user defined conversion
--
diff --git a/src/test/regress/sql/create_aggregate.sql b/src/test/regress/sql/create_aggregate.sql
index 5d42ed057e..4188760c87 100644
--- a/src/test/regress/sql/create_aggregate.sql
+++ b/src/test/regress/sql/create_aggregate.sql
@@ -9,6 +9,11 @@ CREATE AGGREGATE newavg (
initcond1 = '{0,0,0}'
);
+-- test comments
+COMMENT ON AGGREGATE newavg_wrong (int4) IS 'an agg comment';
+COMMENT ON AGGREGATE newavg (int4) IS 'an agg comment';
+COMMENT ON AGGREGATE newavg (int4) IS NULL;
+
-- without finalfunc; test obsolete spellings 'sfunc1' etc
CREATE AGGREGATE newsum (
sfunc1 = int4pl, basetype = int4, stype1 = int4,
@@ -21,3 +26,6 @@ CREATE AGGREGATE newcnt (
initcond = '0'
);
+COMMENT ON AGGREGATE nosuchagg (*) IS 'should fail';
+COMMENT ON AGGREGATE newcnt (*) IS 'an any agg comment';
+COMMENT ON AGGREGATE newcnt (*) IS NULL;
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index d904bacd66..6fa0b91e83 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -32,6 +32,11 @@ CREATE INDEX iix ON ihighway USING btree (name text_ops);
CREATE INDEX six ON shighway USING btree (name text_ops);
+-- test comments
+COMMENT ON INDEX six_wrong IS 'bad index';
+COMMENT ON INDEX six IS 'good index';
+COMMENT ON INDEX six IS NULL;
+
--
-- BTREE ascending/descending cases
--
diff --git a/src/test/regress/sql/create_operator.sql b/src/test/regress/sql/create_operator.sql
index c4251144ca..4167bf3ab8 100644
--- a/src/test/regress/sql/create_operator.sql
+++ b/src/test/regress/sql/create_operator.sql
@@ -32,3 +32,9 @@ CREATE OPERATOR #%# (
procedure = int4fac
);
+-- Test comments
+COMMENT ON OPERATOR ###### (int4, NONE) IS 'bad right unary';
+COMMENT ON OPERATOR #%# (int4, NONE) IS 'right unary';
+COMMENT ON OPERATOR #%# (int4, NONE) IS NULL;
+
+
diff --git a/src/test/regress/sql/create_type.sql b/src/test/regress/sql/create_type.sql
index ddcba72624..6e8fa84b7d 100644
--- a/src/test/regress/sql/create_type.sql
+++ b/src/test/regress/sql/create_type.sql
@@ -69,6 +69,11 @@ CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS '
SELECT * FROM get_default_test();
+-- Test comments
+COMMENT ON TYPE bad IS 'bad comment';
+COMMENT ON TYPE default_test_row IS 'good comment';
+COMMENT ON TYPE default_test_row IS NULL;
+
DROP TYPE default_test_row CASCADE;
DROP TABLE default_test;
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 8c15fc1241..acc82b3e0e 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -19,6 +19,11 @@ CREATE VIEW toyemp AS
SELECT name, age, location, 12*salary AS annualsal
FROM emp;
+-- Test comments
+COMMENT ON VIEW noview IS 'no view';
+COMMENT ON VIEW toyemp IS 'is a view';
+COMMENT ON VIEW toyemp IS NULL;
+
--
-- CREATE OR REPLACE VIEW
--
diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql
index 009588db06..10e4da0baf 100644
--- a/src/test/regress/sql/foreign_key.sql
+++ b/src/test/regress/sql/foreign_key.sql
@@ -50,6 +50,11 @@ CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 text, PRIMARY KEY(ptest1,
CREATE TABLE FKTABLE ( ftest1 int, ftest2 int, ftest3 int, CONSTRAINT constrname FOREIGN KEY(ftest1, ftest2)
REFERENCES PKTABLE MATCH FULL ON DELETE SET NULL ON UPDATE SET NULL);
+-- Test comments
+COMMENT ON CONSTRAINT constrname_wrong ON FKTABLE IS 'fk constraint comment';
+COMMENT ON CONSTRAINT constrname ON FKTABLE IS 'fk constraint comment';
+COMMENT ON CONSTRAINT constrname ON FKTABLE IS NULL;
+
-- Insert test data into PKTABLE
INSERT INTO PKTABLE VALUES (1, 2, 'Test1');
INSERT INTO PKTABLE VALUES (1, 3, 'Test1-2');
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index b4d0186458..9cb7f7c940 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -326,6 +326,10 @@ begin
end;
' language 'plpgsql';
+-- Test comments
+COMMENT ON FUNCTION tg_hub_adjustslots_wrong(bpchar, integer, integer) IS 'function with args';
+COMMENT ON FUNCTION tg_hub_adjustslots(bpchar, integer, integer) IS 'function with args';
+COMMENT ON FUNCTION tg_hub_adjustslots(bpchar, integer, integer) IS NULL;
-- ************************************************************
-- * BEFORE INSERT or UPDATE on HSlot
@@ -1603,4 +1607,4 @@ END;' language 'plpgsql';
SELECT perform_test_func();
SELECT * FROM perform_test;
-drop table perform_test;
\ No newline at end of file
+drop table perform_test;
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index d797144d9d..55c3805bd5 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -19,7 +19,10 @@ create rule rtest_v1_upd as on update to rtest_v1 do instead
where a = old.a;
create rule rtest_v1_del as on delete to rtest_v1 do instead
delete from rtest_t1 where a = old.a;
-
+-- Test comments
+COMMENT ON RULE rtest_v1_bad ON rtest_v1 IS 'bad rule';
+COMMENT ON RULE rtest_v1_del ON rtest_v1 IS 'delete rule';
+COMMENT ON RULE rtest_v1_del ON rtest_v1 IS NULL;
--
-- Tables and rules for the constraint update/delete test
--
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 6f3c1f22dd..07f5765faf 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -37,3 +37,8 @@ SELECT nextval('sequence_test2');
SELECT nextval('sequence_test2');
SELECT nextval('sequence_test2');
+-- Test comments
+COMMENT ON SEQUENCE asdf IS 'won''t work';
+COMMENT ON SEQUENCE sequence_test2 IS 'will work';
+COMMENT ON SEQUENCE sequence_test2 IS NULL;
+
diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql
index 37bfb3bfd5..f766e62554 100644
--- a/src/test/regress/sql/triggers.sql
+++ b/src/test/regress/sql/triggers.sql
@@ -44,6 +44,11 @@ create trigger check_fkeys2_pkey_exist
execute procedure
check_primary_key ('fkey21', 'fkey22', 'pkeys', 'pkey1', 'pkey2');
+-- Test comments
+COMMENT ON TRIGGER check_fkeys2_pkey_bad ON fkeys2 IS 'wrong';
+COMMENT ON TRIGGER check_fkeys2_pkey_exist ON fkeys2 IS 'right';
+COMMENT ON TRIGGER check_fkeys2_pkey_exist ON fkeys2 IS NULL;
+
--
-- For pkeys:
-- ON DELETE/UPDATE (pkey1, pkey2) CASCADE: