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: