mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-15 08:20:16 +08:00
Make operators have their own comments separate from those of the
underlying function; but cause psql's \do to show the underlying function's comment if the operator has no comment of its own, to preserve the useful functionality of the original behavior. Also, implement COMMENT ON SCHEMA. Patch from Rod Taylor.
This commit is contained in:
parent
3389a110d4
commit
f69bc37be8
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/comment.sgml,v 1.18 2002/04/23 02:07:15 tgl Exp $
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/comment.sgml,v 1.19 2002/05/13 17:45:30 tgl Exp $
|
||||
PostgreSQL documentation
|
||||
-->
|
||||
|
||||
@ -23,13 +23,20 @@ PostgreSQL documentation
|
||||
<synopsis>
|
||||
COMMENT ON
|
||||
[
|
||||
[ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW ] <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
TABLE <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
|
||||
AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable>) |
|
||||
FUNCTION <replaceable class="PARAMETER">func_name</replaceable> (<replaceable class="PARAMETER">arg1</replaceable>, <replaceable class="PARAMETER">arg2</replaceable>, ...) |
|
||||
OPERATOR <replaceable class="PARAMETER">op</replaceable> (<replaceable class="PARAMETER">leftoperand_type</replaceable> <replaceable class="PARAMETER">rightoperand_type</replaceable>) |
|
||||
DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
FUNCTION <replaceable class="PARAMETER">func_name</replaceable> (<replaceable class="PARAMETER">arg1_type</replaceable>, <replaceable class="PARAMETER">arg2_type</replaceable>, ...) |
|
||||
INDEX <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
OPERATOR <replaceable class="PARAMETER">op</replaceable> (<replaceable class="PARAMETER">leftoperand_type</replaceable>, <replaceable class="PARAMETER">rightoperand_type</replaceable>) |
|
||||
RULE <replaceable class="PARAMETER">rule_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> |
|
||||
TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable>
|
||||
SCHEMA <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
SEQUENCE <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> |
|
||||
TYPE <replaceable class="PARAMETER">object_name</replaceable> |
|
||||
VIEW <replaceable class="PARAMETER">object_name</replaceable>
|
||||
] IS <replaceable class="PARAMETER">'text'</replaceable>
|
||||
</synopsis>
|
||||
|
||||
@ -49,8 +56,9 @@ COMMENT ON
|
||||
<listitem>
|
||||
<para>
|
||||
The name of the object to be be commented. Names of tables,
|
||||
indexes, sequences, views, types, domains, functions, aggregates,
|
||||
and operators may be schema-qualified.
|
||||
aggregates, domains, functions, indexes, operators, sequences, types,
|
||||
and views
|
||||
may be schema-qualified.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -116,13 +124,15 @@ COMMENT
|
||||
Comments are automatically dropped when the object is dropped.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
It should be noted that there is presently no security mechanism
|
||||
There is presently no security mechanism
|
||||
for comments: any user connected to a database can see all the comments
|
||||
for objects in that database (although only superusers can change
|
||||
comments for objects that they don't own). Therefore, don't put
|
||||
security-critical information in comments.
|
||||
</para>
|
||||
</note>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="R1-SQL-COMMENT-2">
|
||||
@ -130,10 +140,16 @@ COMMENT
|
||||
Usage
|
||||
</title>
|
||||
<para>
|
||||
Comment the table <literal>mytable</literal>:
|
||||
Attach a comment to the table <literal>mytable</literal>:
|
||||
|
||||
<programlisting>
|
||||
COMMENT ON mytable IS 'This is my table.';
|
||||
COMMENT ON TABLE mytable IS 'This is my table.';
|
||||
</programlisting>
|
||||
|
||||
Remove it again:
|
||||
|
||||
<programlisting>
|
||||
COMMENT ON TABLE mytable IS NULL;
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
@ -141,19 +157,21 @@ COMMENT ON mytable IS 'This is my table.';
|
||||
Some more examples:
|
||||
|
||||
<programlisting>
|
||||
COMMENT ON AGGREGATE my_aggregate (double precision) IS 'Computes sample variance';
|
||||
COMMENT ON COLUMN my_table.my_field IS 'Employee ID number';
|
||||
COMMENT ON DATABASE my_database IS 'Development Database';
|
||||
COMMENT ON DOMAIN my_domain IS 'Email Address Domain';
|
||||
COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee id';
|
||||
COMMENT ON SEQUENCE my_sequence IS 'Used to generate primary keys';
|
||||
COMMENT ON TABLE my_table IS 'Employee Information';
|
||||
COMMENT ON TYPE my_type IS 'Complex Number support';
|
||||
COMMENT ON VIEW my_view IS 'View of departmental costs';
|
||||
COMMENT ON COLUMN my_table.my_field IS 'Employee ID number';
|
||||
COMMENT ON AGGREGATE my_aggregate (double precision) IS 'Computes sample variance';
|
||||
COMMENT ON FUNCTION my_function (timestamp) IS 'Returns Roman Numeral';
|
||||
COMMENT ON OPERATOR ^ (text, text) IS 'Performs intersection of two text';
|
||||
COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee id';
|
||||
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 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';
|
||||
COMMENT ON TABLE my_schema.my_table IS 'Employee Information';
|
||||
COMMENT ON TRIGGER my_trigger ON my_table IS 'Used for R.I.';
|
||||
COMMENT ON TYPE complex IS 'Complex Number datatype';
|
||||
COMMENT ON VIEW my_view IS 'View of departmental costs';
|
||||
</programlisting>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Copyright (c) 1999-2001, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.45 2002/04/27 03:45:00 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.46 2002/05/13 17:45:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -50,6 +50,7 @@
|
||||
static void CommentRelation(int objtype, List *relname, char *comment);
|
||||
static void CommentAttribute(List *qualname, char *comment);
|
||||
static void CommentDatabase(List *qualname, char *comment);
|
||||
static void CommentNamespace(List *qualname, char *comment);
|
||||
static void CommentRule(List *qualname, char *comment);
|
||||
static void CommentType(List *typename, char *comment);
|
||||
static void CommentAggregate(List *aggregate, List *arguments, char *comment);
|
||||
@ -99,6 +100,9 @@ CommentObject(CommentStmt *stmt)
|
||||
case TRIGGER:
|
||||
CommentTrigger(stmt->objname, stmt->comment);
|
||||
break;
|
||||
case SCHEMA:
|
||||
CommentNamespace(stmt->objname, stmt->comment);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "An attempt was made to comment on a unknown type: %d",
|
||||
stmt->objtype);
|
||||
@ -332,22 +336,22 @@ CommentRelation(int objtype, List *relname, char *comment)
|
||||
{
|
||||
case INDEX:
|
||||
if (relation->rd_rel->relkind != RELKIND_INDEX)
|
||||
elog(ERROR, "relation '%s' is not an index",
|
||||
elog(ERROR, "relation \"%s\" is not an index",
|
||||
RelationGetRelationName(relation));
|
||||
break;
|
||||
case TABLE:
|
||||
if (relation->rd_rel->relkind != RELKIND_RELATION)
|
||||
elog(ERROR, "relation '%s' is not a table",
|
||||
elog(ERROR, "relation \"%s\" is not a table",
|
||||
RelationGetRelationName(relation));
|
||||
break;
|
||||
case VIEW:
|
||||
if (relation->rd_rel->relkind != RELKIND_VIEW)
|
||||
elog(ERROR, "relation '%s' is not a view",
|
||||
elog(ERROR, "relation \"%s\" is not a view",
|
||||
RelationGetRelationName(relation));
|
||||
break;
|
||||
case SEQUENCE:
|
||||
if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
|
||||
elog(ERROR, "relation '%s' is not a sequence",
|
||||
elog(ERROR, "relation \"%s\" is not a sequence",
|
||||
RelationGetRelationName(relation));
|
||||
break;
|
||||
}
|
||||
@ -400,7 +404,7 @@ CommentAttribute(List *qualname, char *comment)
|
||||
|
||||
attnum = get_attnum(RelationGetRelid(relation), attrname);
|
||||
if (attnum == InvalidAttrNumber)
|
||||
elog(ERROR, "'%s' is not an attribute of class '%s'",
|
||||
elog(ERROR, "\"%s\" is not an attribute of class \"%s\"",
|
||||
attrname, RelationGetRelationName(relation));
|
||||
|
||||
/* Create the comment using the relation's oid */
|
||||
@ -451,13 +455,13 @@ CommentDatabase(List *qualname, char *comment)
|
||||
/* Validate database exists, and fetch the db oid */
|
||||
|
||||
if (!HeapTupleIsValid(dbtuple))
|
||||
elog(ERROR, "database '%s' does not exist", database);
|
||||
elog(ERROR, "database \"%s\" does not exist", database);
|
||||
oid = dbtuple->t_data->t_oid;
|
||||
|
||||
/* Allow if the user matches the database dba or is a superuser */
|
||||
|
||||
if (!(superuser() || is_dbadmin(oid)))
|
||||
elog(ERROR, "you are not permitted to comment on database '%s'",
|
||||
elog(ERROR, "you are not permitted to comment on database \"%s\"",
|
||||
database);
|
||||
|
||||
/* Create the comments with the pg_database oid */
|
||||
@ -470,6 +474,51 @@ CommentDatabase(List *qualname, char *comment)
|
||||
heap_close(pg_database, AccessShareLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* CommentNamespace --
|
||||
*
|
||||
* This routine is used to add/drop any user-comments a user might
|
||||
* have regarding the specified namespace. The routine will check
|
||||
* security for owner permissions, and, if succesful, will then
|
||||
* attempt to find the oid of the namespace specified. Once found,
|
||||
* a comment is added/dropped using the CreateComments() routine.
|
||||
*/
|
||||
static void
|
||||
CommentNamespace(List *qualname, char *comment)
|
||||
{
|
||||
Oid oid;
|
||||
Oid classoid;
|
||||
HeapTuple tp;
|
||||
char *namespace;
|
||||
|
||||
if (length(qualname) != 1)
|
||||
elog(ERROR, "CommentSchema: schema name may not be qualified");
|
||||
namespace = strVal(lfirst(qualname));
|
||||
|
||||
tp = SearchSysCache(NAMESPACENAME,
|
||||
CStringGetDatum(namespace),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "CommentSchema: Schema \"%s\" could not be found",
|
||||
namespace);
|
||||
|
||||
oid = tp->t_data->t_oid;
|
||||
|
||||
/* Check object security */
|
||||
if (!pg_namespace_ownercheck(oid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, namespace);
|
||||
|
||||
/* pg_namespace doesn't have a hard-coded OID, so must look it up */
|
||||
classoid = get_relname_relid(NamespaceRelationName, PG_CATALOG_NAMESPACE);
|
||||
Assert(OidIsValid(classoid));
|
||||
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
CreateComments(oid, classoid, 0, comment);
|
||||
|
||||
/* Cleanup */
|
||||
ReleaseSysCache(tp);
|
||||
}
|
||||
|
||||
/*
|
||||
* CommentRule --
|
||||
*
|
||||
@ -528,12 +577,12 @@ CommentRule(List *qualname, char *comment)
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(ERROR, "rule '%s' does not exist", rulename);
|
||||
elog(ERROR, "rule \"%s\" does not exist", rulename);
|
||||
reloid = ruleoid = 0; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
if (HeapTupleIsValid(tuple = heap_getnext(scanDesc, 0)))
|
||||
elog(ERROR, "There are multiple rules '%s'"
|
||||
elog(ERROR, "There are multiple rules \"%s\""
|
||||
"\n\tPlease specify a relation name as well as a rule name",
|
||||
rulename);
|
||||
|
||||
@ -561,7 +610,7 @@ CommentRule(List *qualname, char *comment)
|
||||
PointerGetDatum(rulename),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "rule '%s' does not exist", rulename);
|
||||
elog(ERROR, "rule \"%s\" does not exist", rulename);
|
||||
Assert(reloid == ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class);
|
||||
ruleoid = tuple->t_data->t_oid;
|
||||
ReleaseSysCache(tuple);
|
||||
@ -574,7 +623,6 @@ CommentRule(List *qualname, char *comment)
|
||||
aclcheck_error(aclcheck, rulename);
|
||||
|
||||
/* pg_rewrite doesn't have a hard-coded OID, so must look it up */
|
||||
|
||||
classoid = get_relname_relid(RewriteRelationName, PG_CATALOG_NAMESPACE);
|
||||
Assert(OidIsValid(classoid));
|
||||
|
||||
@ -689,12 +737,7 @@ CommentProc(List *function, List *arguments, char *comment)
|
||||
* its name and its argument list which defines the left and right
|
||||
* hand types the operator will operate on. The argument list is
|
||||
* expected to be a couple of parse nodes pointed to be a List
|
||||
* object. If the comments string is empty, the associated comment
|
||||
* is dropped.
|
||||
*
|
||||
* NOTE: we actually attach the comment to the procedure that underlies
|
||||
* the operator. This is a feature, not a bug: we want the same comment
|
||||
* to be visible for both operator and function.
|
||||
* object.
|
||||
*/
|
||||
static void
|
||||
CommentOperator(List *opername, List *arguments, char *comment)
|
||||
@ -702,27 +745,22 @@ CommentOperator(List *opername, List *arguments, char *comment)
|
||||
TypeName *typenode1 = (TypeName *) lfirst(arguments);
|
||||
TypeName *typenode2 = (TypeName *) lsecond(arguments);
|
||||
Oid oid;
|
||||
Oid classoid;
|
||||
|
||||
/* Look up the operator */
|
||||
|
||||
oid = LookupOperNameTypeNames(opername, typenode1, typenode2,
|
||||
"CommentOperator");
|
||||
|
||||
/* Valid user's ability to comment on this operator */
|
||||
|
||||
if (!pg_oper_ownercheck(oid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(opername));
|
||||
|
||||
/* Get the procedure associated with the operator */
|
||||
|
||||
oid = get_opcode(oid);
|
||||
if (oid == InvalidOid)
|
||||
elog(ERROR, "operator '%s' does not have an underlying function",
|
||||
NameListToString(opername));
|
||||
/* pg_operator doesn't have a hard-coded OID, so must look it up */
|
||||
classoid = get_relname_relid(OperatorRelationName, PG_CATALOG_NAMESPACE);
|
||||
Assert(OidIsValid(classoid));
|
||||
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
|
||||
CreateComments(oid, RelOid_pg_proc, 0, comment);
|
||||
CreateComments(oid, classoid, 0, comment);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -784,7 +822,7 @@ CommentTrigger(List *qualname, char *comment)
|
||||
/* If no trigger exists for the relation specified, notify user */
|
||||
|
||||
if (!HeapTupleIsValid(triggertuple))
|
||||
elog(ERROR, "trigger '%s' for relation '%s' does not exist",
|
||||
elog(ERROR, "trigger \"%s\" for relation \"%s\" does not exist",
|
||||
trigname, RelationGetRelationName(relation));
|
||||
|
||||
oid = triggertuple->t_data->t_oid;
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.314 2002/05/12 20:10:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.315 2002/05/13 17:45:30 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -2278,6 +2278,7 @@ CommentStmt: COMMENT ON comment_type any_name IS comment_text
|
||||
|
||||
comment_type: COLUMN { $$ = COLUMN; }
|
||||
| DATABASE { $$ = DATABASE; }
|
||||
| SCHEMA { $$ = SCHEMA; }
|
||||
| INDEX { $$ = INDEX; }
|
||||
| SEQUENCE { $$ = SEQUENCE; }
|
||||
| TABLE { $$ = TABLE; }
|
||||
|
@ -22,7 +22,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.259 2002/05/10 22:36:26 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.260 2002/05/13 17:45:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -2686,7 +2686,6 @@ dumpNamespaces(Archive *fout, NamespaceInfo *nsinfo, int numNamespaces)
|
||||
nsinfo[i].usename, "SCHEMA", NULL,
|
||||
q->data, delq->data, NULL, NULL, NULL);
|
||||
|
||||
#ifdef NOTYET /* suppress till COMMENT ON SCHEMA works */
|
||||
/*** Dump Schema Comments ***/
|
||||
resetPQExpBuffer(q);
|
||||
appendPQExpBuffer(q, "SCHEMA %s",
|
||||
@ -2694,7 +2693,6 @@ dumpNamespaces(Archive *fout, NamespaceInfo *nsinfo, int numNamespaces)
|
||||
dumpComment(fout, q->data,
|
||||
NULL, nsinfo[i].usename,
|
||||
nsinfo[i].oid, "pg_namespace", 0, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
destroyPQExpBuffer(q);
|
||||
@ -3396,7 +3394,7 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo)
|
||||
/*** Dump Function Comments ***/
|
||||
|
||||
resetPQExpBuffer(q);
|
||||
appendPQExpBuffer(q, "FUNCTION %s ", fn->data);
|
||||
appendPQExpBuffer(q, "FUNCTION %s", fn->data);
|
||||
dumpComment(fout, q->data,
|
||||
finfo->pronamespace->nspname, finfo->usename,
|
||||
finfo->oid, "pg_proc", 0, NULL);
|
||||
@ -3655,11 +3653,13 @@ dumpOneOpr(Archive *fout, OprInfo *oprinfo,
|
||||
q->data, delq->data,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
/*
|
||||
* Note: no need to dump operator comment; we expect that the comment
|
||||
* is attached to the underlying function instead. (If the function
|
||||
* isn't getting dumped ... you lose.)
|
||||
*/
|
||||
/*** Dump Operator Comments ***/
|
||||
|
||||
resetPQExpBuffer(q);
|
||||
appendPQExpBuffer(q, "OPERATOR %s", oprid->data);
|
||||
dumpComment(fout, q->data,
|
||||
oprinfo->oprnamespace->nspname, oprinfo->usename,
|
||||
oprinfo->oid, "pg_operator", 0, NULL);
|
||||
|
||||
PQclear(res);
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright 2000 by PostgreSQL Global Development Group
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.53 2002/04/25 02:56:56 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.54 2002/05/13 17:45:30 tgl Exp $
|
||||
*/
|
||||
#include "postgres_fe.h"
|
||||
#include "describe.h"
|
||||
@ -210,7 +210,8 @@ describeOperators(const char *name)
|
||||
" CASE WHEN o.oprkind='l' THEN NULL ELSE format_type(o.oprleft, NULL) END AS \"%s\",\n"
|
||||
" CASE WHEN o.oprkind='r' THEN NULL ELSE format_type(o.oprright, NULL) END AS \"%s\",\n"
|
||||
" format_type(o.oprresult, NULL) AS \"%s\",\n"
|
||||
" obj_description(o.oprcode, 'pg_proc') AS \"%s\"\n"
|
||||
" coalesce(obj_description(o.oid, 'pg_operator'),"
|
||||
" obj_description(o.oprcode, 'pg_proc')) AS \"%s\"\n"
|
||||
"FROM pg_operator o\n",
|
||||
_("Name"), _("Left arg type"), _("Right arg type"),
|
||||
_("Result type"), _("Description"));
|
||||
@ -359,10 +360,9 @@ objectDescription(const char *object)
|
||||
" FROM pg_proc p\n"
|
||||
" WHERE (p.pronargs = 0 or oidvectortypes(p.proargtypes) <> '') AND NOT p.proisagg\n"
|
||||
|
||||
/* Operator descriptions (must get comment via associated function) */
|
||||
/* Operator descriptions (only if operator has its own comment) */
|
||||
"UNION ALL\n"
|
||||
" SELECT CAST(o.oprcode AS oid) as oid,\n"
|
||||
" (SELECT oid FROM pg_class WHERE relname = 'pg_proc') as tableoid,\n"
|
||||
" SELECT o.oid as oid, o.tableoid as tableoid,\n"
|
||||
" CAST(o.oprname AS text) as name, CAST('%s' AS text) as object\n"
|
||||
" FROM pg_operator o\n"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user