*** a/contrib/Makefile --- b/contrib/Makefile *************** *** 15,20 **** SUBDIRS = \ --- 15,21 ---- dblink \ dict_int \ dict_xsyn \ + dummy_esp \ earthdistance \ fuzzystrmatch \ hstore \ *** /dev/null --- b/contrib/dummy_esp/Makefile *************** *** 0 **** --- 1,14 ---- + # contrib/dummy_esp/Makefile + + MODULES = dummy_esp + + ifdef USE_PGXS + PG_CONFIG = pg_config + PGXS := $(shell $(PG_CONFIG) --pgxs) + include $(PGXS) + else + subdir = contrib/dummy_esp + top_builddir = ../.. + include $(top_builddir)/src/Makefile.global + include $(top_srcdir)/contrib/contrib-global.mk + endif *** /dev/null --- b/contrib/dummy_esp/dummy_esp.c *************** *** 0 **** --- 1,56 ---- + /* + * dummy_esp.c + * + * A dummy plugin of external security provider with security label. + * + * This module does not provide something worthful from security perspective, + * but being used to regression test independent from the platform specific + * features like SELinux. + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ + #include "postgres.h" + + #include "commands/seclabel.h" + + PG_MODULE_MAGIC; + + /* Entrypoint of the module */ + void _PG_init(void); + + /* + * dummy_object_relabel + * + * It shall be invoked when user tries to assign security label of + * the database object. + */ + void + dummy_object_relabel(const ObjectAddress *object, const char *seclabel) + { + if (strcmp(seclabel, "unclassified") == 0 || + strcmp(seclabel, "classified") == 0) + return; + + if (strcmp(seclabel, "secret") == 0 || + strcmp(seclabel, "top secret") == 0) + { + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("only superuser can set '%s' label", seclabel))); + return; + } + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("'%s' is not a valid security label", seclabel))); + } + + /* + * Entrypoint of the module + */ + void + _PG_init(void) + { + register_label_provider("dummy", dummy_object_relabel); + } *** a/doc/src/sgml/catalogs.sgml --- b/doc/src/sgml/catalogs.sgml *************** *** 5964,5969 **** --- 5964,5974 ---- + pg_seclabels + security labels + + + pg_settings parameter settings *************** *** 6870,6876 **** --- 6875,6965 ---- + + <structname>pg_seclabels</structname> + + + pg_seclabels + + + + The view pg_seclabels provides references to + security label of database objects with their text identifiers. + + + + <structname>pg_seclabels</> Columns + + + + Name + Type + References + Description + + + + + objoid + oid + any OID column + The OID of the object this security label assigned on + + + classoid + oid + pg_class.oid + The OID of the system catalog this object appears in + + + objsubid + int4 + + + For a security label on a table column, this is the column number (the + objoid and classoid refer to + the table itself). For all other object types, this column is + zero. + + + + objtype + text + + text identifier of the object type + + + objnamespace + oid + pg_namespace.oid + + The OID of the namespace that contains this object. + Maybe NULL, if object does not have its namespace. + + + + objname + text + + The name of the object this security label assigned on + + + provider + text + pg_seclabel.provider + The label provider associated with this label. + + + label + text + pg_seclabel.label + The security label applied to this object. + + + +
+
<structname>pg_settings</structname> *** a/doc/src/sgml/user-manag.sgml --- b/doc/src/sgml/user-manag.sgml *************** *** 502,505 **** DROP ROLE name; --- 502,536 ---- + + External Security Providers + + + PostgreSQL allows third party plugins to + make their access control decision in addition to the default database + privilege mechanism. We call these plugins external security providers. + The version 9.1.0 or later provide various kind of + security hooks on strategic points of code paths, then the external + security providers shall be invoked via the hooks. + + + The major purpose to support external security providers is porting + mandatory access control (MAC) features; + such as SELinux. + Unlike the default database privilege mechanism, it makes access + control decision using a security label of database objects being + referenced and a centralized security policy. + PostgreSQL provides a set of facilities + to manage security labels of database objects as built-in feature. + The pg_seclabel + system catalog enables to assign security labels + on database objects, command + allows us to manage security labels, and some of security hooks allows + to label database objects on its creation time. + + + We don't mention about detail of the individual security module here, + so please reference their own documentation to see any more information. + + *** a/src/backend/catalog/system_views.sql --- b/src/backend/catalog/system_views.sql *************** *** 160,165 **** CREATE VIEW pg_prepared_xacts AS --- 160,274 ---- CREATE VIEW pg_prepared_statements AS SELECT * FROM pg_prepared_statement() AS P; + CREATE VIEW pg_seclabels AS + SELECT + l.objoid, l.classoid, l.objsubid, + CASE WHEN rel.relkind = 'r' THEN 'table'::text + WHEN rel.relkind = 'v' THEN 'view'::text + WHEN rel.relkind = 'S' THEN 'sequence'::text END AS objtype, + rel.relnamespace AS objnamespace, + CASE WHEN pg_table_is_visible(rel.oid) + THEN quote_ident(rel.relname) + ELSE quote_ident(nsp.nspname) || '.' || quote_ident(rel.relname) + END AS objname, + l.provider, l.label + FROM + pg_seclabel l + JOIN pg_class rel ON l.classoid = rel.tableoid AND l.objoid = rel.oid + JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + WHERE + l.objsubid = 0 + UNION ALL + SELECT + l.objoid, l.classoid, l.objsubid, + 'column'::text AS objtype, + rel.relnamespace AS objnamespace, + CASE WHEN pg_table_is_visible(rel.oid) + THEN quote_ident(rel.relname) + ELSE quote_ident(nsp.nspname) || '.' || quote_ident(rel.relname) + END || '.' || att.attname AS objname, + l.provider, l.label + FROM + pg_seclabel l + JOIN pg_class rel ON l.classoid = rel.tableoid AND l.objoid = rel.oid + JOIN pg_attribute att + ON rel.oid = att.attrelid AND l.objsubid = att.attnum + JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + WHERE + l.objsubid != 0 + UNION ALL + SELECT + l.objoid, l.classoid, l.objsubid, + CASE WHEN pro.proisagg = true THEN 'aggregate'::text + WHEN pro.proisagg = false THEN 'function'::text + END AS objtype, + pro.pronamespace AS objnamespace, + CASE WHEN pg_function_is_visible(pro.oid) + THEN quote_ident(pro.proname) + ELSE quote_ident(nsp.nspname) || '.' || quote_ident(pro.proname) + END || '(' || pg_catalog.pg_get_function_arguments(pro.oid) || ')' AS objname, + l.provider, l.label + FROM + pg_seclabel l + JOIN pg_proc pro ON l.classoid = pro.tableoid AND l.objoid = pro.oid + JOIN pg_namespace nsp ON pro.pronamespace = nsp.oid + WHERE + l.objsubid = 0 + UNION ALL + SELECT + l.objoid, l.classoid, l.objsubid, + CASE WHEN typ.typtype = 'd' THEN 'domain'::text + ELSE 'type'::text END AS objtype, + typ.typnamespace AS objnamespace, + CASE WHEN pg_type_is_visible(typ.oid) + THEN quote_ident(typ.typname) + ELSE quote_ident(nsp.nspname) || '.' || quote_ident(typ.typname) + END AS objname, + l.provider, l.label + FROM + pg_seclabel l + JOIN pg_type typ ON l.classoid = typ.tableoid AND l.objoid = typ.oid + JOIN pg_namespace nsp ON typ.typnamespace = nsp.oid + WHERE + l.objsubid = 0 + UNION ALL + SELECT + l.objoid, l.classoid, l.objsubid, + 'large object'::text AS objtype, + NULL::oid AS objnamespace, + l.objoid::text AS objname, + l.provider, l.label + FROM + pg_seclabel l + JOIN pg_largeobject_metadata lom ON l.objoid = lom.oid + WHERE + l.classoid = 'pg_catalog.pg_largeobject'::regclass AND l.objsubid = 0 + UNION ALL + SELECT + l.objoid, l.classoid, l.objsubid, + 'language'::text AS objtype, + NULL::oid AS objnamespace, + quote_ident(lan.lanname) AS objname, + l.provider, l.label + FROM + pg_seclabel l + JOIN pg_language lan ON l.classoid = lan.tableoid AND l.objoid = lan.oid + WHERE + l.objsubid = 0 + UNION ALL + SELECT + l.objoid, l.classoid, l.objsubid, + 'schema'::text AS objtype, + nsp.oid AS objnamespace, + quote_ident(nsp.nspname) AS objname, + l.provider, l.label + FROM + pg_seclabel l + JOIN pg_namespace nsp ON l.classoid = nsp.tableoid AND l.objoid = nsp.oid + WHERE + l.objsubid = 0 + ; + CREATE VIEW pg_settings AS SELECT * FROM pg_show_all_settings() AS A; *** a/src/bin/pg_dump/pg_dump.c --- b/src/bin/pg_dump/pg_dump.c *************** *** 70,75 **** typedef struct --- 70,83 ---- int objsubid; /* subobject (table column #) */ } CommentItem; + typedef struct + { + const char *provider; /* label provider of this security label */ + const char *label; /* security label for an object */ + Oid classoid; /* object class (catalog OID) */ + Oid objoid; /* object OID */ + int objsubid; /* subobject (table column #) */ + } SecLabelItem; /* global decls */ bool g_verbose; /* User wants verbose narration of our *************** *** 125,131 **** static int binary_upgrade = 0; static int disable_dollar_quoting = 0; static int dump_inserts = 0; static int column_inserts = 0; ! static int security_label = 0; static void help(const char *progname); --- 133,139 ---- static int disable_dollar_quoting = 0; static int dump_inserts = 0; static int column_inserts = 0; ! static int no_security_label = 0; static void help(const char *progname); *************** *** 142,147 **** static void dumpComment(Archive *fout, const char *target, --- 150,161 ---- static int findComments(Archive *fout, Oid classoid, Oid objoid, CommentItem **items); static int collectComments(Archive *fout, CommentItem **items); + static void dumpSecLabel(Archive *fout, const char *target, + const char *namespace, const char *owner, + CatalogId catalogId, int subid, DumpId dumpId); + static int findSecLabels(Archive *fout, Oid classoid, Oid objoid, + SecLabelItem **items); + static int collectSecLabels(Archive *fout, SecLabelItem **items); static void dumpDumpableObject(Archive *fout, DumpableObject *dobj); static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo); static void dumpType(Archive *fout, TypeInfo *tyinfo); *************** *** 184,193 **** static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, const char *tag, const char *nspname, const char *owner, const char *acls); - static void dumpSecLabel(Archive *fout, CatalogId objCatId, DumpId objDumpId, - const char *type, const char *target, const char *nspname, - const char *owner); - static void getDependencies(void); static void getDomainConstraints(TypeInfo *tyinfo); static void getTableData(TableInfo *tblinfo, int numTables, bool oids); --- 198,203 ---- *************** *** 305,311 **** main(int argc, char **argv) {"quote-all-identifiers", no_argument, "e_all_identifiers, 1}, {"role", required_argument, NULL, 3}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ! {"security-label", no_argument, &security_label, 1}, {NULL, 0, NULL, 0} }; --- 315,321 ---- {"quote-all-identifiers", no_argument, "e_all_identifiers, 1}, {"role", required_argument, NULL, 3}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ! {"no-security-label", no_argument, &no_security_label, 1}, {NULL, 0, NULL, 0} }; *************** *** 454,461 **** main(int argc, char **argv) outputNoTablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; ! else if (strcmp(optarg, "security-label") == 0) ! security_label = 1; else { fprintf(stderr, --- 464,471 ---- outputNoTablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; ! else if (strcmp(optarg, "no-security-label") == 0) ! no_security_label = 1; else { fprintf(stderr, *************** *** 653,663 **** main(int argc, char **argv) /* * Disables security label support if server version < v9.1.x */ ! if (security_label && g_fout->remoteVersion < 90100) ! { ! write_msg(NULL, "Server does not support security labels\n"); ! security_label = 0; ! } /* * Start serializable transaction to dump consistent data. --- 663,670 ---- /* * Disables security label support if server version < v9.1.x */ ! if (!no_security_label && g_fout->remoteVersion < 90100) ! no_security_label = 1; /* * Start serializable transaction to dump consistent data. *************** *** 856,862 **** help(const char *progname) printf(_(" --no-tablespaces do not dump tablespace assignments\n")); printf(_(" --quote-all-identifiers quote all identifiers, even if not keywords\n")); printf(_(" --role=ROLENAME do SET ROLE before dump\n")); ! printf(_(" --security-label also dump security labels\n")); printf(_(" --use-set-session-authorization\n" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); --- 863,869 ---- printf(_(" --no-tablespaces do not dump tablespace assignments\n")); printf(_(" --quote-all-identifiers quote all identifiers, even if not keywords\n")); printf(_(" --role=ROLENAME do SET ROLE before dump\n")); ! printf(_(" --no-security-label do not dump security label assignments\n")); printf(_(" --use-set-session-authorization\n" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); *************** *** 2076,2081 **** dumpBlob(Archive *AH, BlobInfo *binfo) --- 2083,2093 ---- NULL, binfo->rolname, binfo->dobj.catId, 0, binfo->dobj.dumpId); + /* Dump security label if any */ + dumpSecLabel(AH, cquery->data, + NULL, binfo->rolname, + binfo->dobj.catId, 0, binfo->dobj.dumpId); + /* Dump ACL if any */ if (binfo->blobacl) dumpACL(AH, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT", *************** *** 6587,6598 **** dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) nspinfo->dobj.dependencies, nspinfo->dobj.nDeps, NULL, NULL); ! /* Dump Schema Comments */ resetPQExpBuffer(q); appendPQExpBuffer(q, "SCHEMA %s", qnspname); dumpComment(fout, q->data, NULL, nspinfo->rolname, nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA", qnspname, NULL, nspinfo->dobj.name, NULL, --- 6599,6613 ---- nspinfo->dobj.dependencies, nspinfo->dobj.nDeps, NULL, NULL); ! /* Dump Schema Comments and Security Labels */ resetPQExpBuffer(q); appendPQExpBuffer(q, "SCHEMA %s", qnspname); dumpComment(fout, q->data, NULL, nspinfo->rolname, nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); + dumpSecLabel(fout, q->data, + NULL, nspinfo->rolname, + nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA", qnspname, NULL, nspinfo->dobj.name, NULL, *************** *** 6717,6729 **** dumpEnumType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, NULL, NULL); ! /* Dump Type Comments */ resetPQExpBuffer(q); appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name)); dumpComment(fout, q->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); PQclear(res); destroyPQExpBuffer(q); --- 6732,6747 ---- tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, NULL, NULL); ! /* Dump Type Comments and Security Labels */ resetPQExpBuffer(q); appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name)); dumpComment(fout, q->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + dumpSecLabel(fout, q->data, + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); PQclear(res); destroyPQExpBuffer(q); *************** *** 7093,7105 **** dumpBaseType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, NULL, NULL); ! /* Dump Type Comments */ resetPQExpBuffer(q); appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name)); dumpComment(fout, q->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); PQclear(res); destroyPQExpBuffer(q); --- 7111,7126 ---- tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, NULL, NULL); ! /* Dump Type Comments and Security Labels */ resetPQExpBuffer(q); appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name)); dumpComment(fout, q->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + dumpSecLabel(fout, q->data, + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); PQclear(res); destroyPQExpBuffer(q); *************** *** 7217,7229 **** dumpDomain(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, NULL, NULL); ! /* Dump Domain Comments */ resetPQExpBuffer(q); appendPQExpBuffer(q, "DOMAIN %s", fmtId(tyinfo->dobj.name)); dumpComment(fout, q->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); --- 7238,7253 ---- tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, NULL, NULL); ! /* Dump Domain Comments and Security Labels */ resetPQExpBuffer(q); appendPQExpBuffer(q, "DOMAIN %s", fmtId(tyinfo->dobj.name)); dumpComment(fout, q->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + dumpSecLabel(fout, q->data, + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); *************** *** 7323,7335 **** dumpCompositeType(Archive *fout, TypeInfo *tyinfo) NULL, NULL); ! /* Dump Type Comments */ resetPQExpBuffer(q); appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name)); dumpComment(fout, q->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); PQclear(res); destroyPQExpBuffer(q); --- 7347,7362 ---- NULL, NULL); ! /* Dump Type Comments and Security Labels */ resetPQExpBuffer(q); appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name)); dumpComment(fout, q->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + dumpSecLabel(fout, q->data, + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); PQclear(res); destroyPQExpBuffer(q); *************** *** 7647,7658 **** dumpProcLang(Archive *fout, ProcLangInfo *plang) plang->dobj.dependencies, plang->dobj.nDeps, NULL, NULL); ! /* Dump Proc Lang Comments */ resetPQExpBuffer(defqry); appendPQExpBuffer(defqry, "LANGUAGE %s", qlanname); dumpComment(fout, defqry->data, NULL, "", plang->dobj.catId, 0, plang->dobj.dumpId); if (plang->lanpltrusted) dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE", --- 7674,7688 ---- plang->dobj.dependencies, plang->dobj.nDeps, NULL, NULL); ! /* Dump Proc Lang Comments and Security Labels */ resetPQExpBuffer(defqry); appendPQExpBuffer(defqry, "LANGUAGE %s", qlanname); dumpComment(fout, defqry->data, NULL, "", plang->dobj.catId, 0, plang->dobj.dumpId); + dumpSecLabel(fout, defqry->data, + NULL, "", + plang->dobj.catId, 0, plang->dobj.dumpId); if (plang->lanpltrusted) dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE", *************** *** 8208,8219 **** dumpFunc(Archive *fout, FuncInfo *finfo) finfo->dobj.dependencies, finfo->dobj.nDeps, NULL, NULL); ! /* Dump Function Comments */ resetPQExpBuffer(q); appendPQExpBuffer(q, "FUNCTION %s", funcsig); dumpComment(fout, q->data, finfo->dobj.namespace->dobj.name, finfo->rolname, finfo->dobj.catId, 0, finfo->dobj.dumpId); dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION", funcsig, NULL, funcsig_tag, --- 8238,8252 ---- finfo->dobj.dependencies, finfo->dobj.nDeps, NULL, NULL); ! /* Dump Function Comments and Security Labels */ resetPQExpBuffer(q); appendPQExpBuffer(q, "FUNCTION %s", funcsig); dumpComment(fout, q->data, finfo->dobj.namespace->dobj.name, finfo->rolname, finfo->dobj.catId, 0, finfo->dobj.dumpId); + dumpSecLabel(fout, q->data, + finfo->dobj.namespace->dobj.name, finfo->rolname, + finfo->dobj.catId, 0, finfo->dobj.dumpId); dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION", funcsig, NULL, funcsig_tag, *************** *** 9711,9716 **** dumpAgg(Archive *fout, AggInfo *agginfo) --- 9744,9752 ---- dumpComment(fout, q->data, agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname, agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); + dumpSecLabel(fout, q->data, + agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname, + agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); /* * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL *************** *** 10461,10566 **** dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, /* * dumpSecLabel * ! * It dumps any security labels associated with the object handed ! * to this routine. The routine takes a constant character string ! * for the target part of the security-label command, plus the ! * namespace and owner of the object (for labeling the ArchiveEntry), ! * plus catalogId which is lookup key for pg_seclabel, and dumpId ! * for the object. * If a matching pg_seclabel entry is found, it is dumped. */ static void ! dumpSecLabel(Archive *fout, CatalogId objCatId, DumpId objDumpId, ! const char *type, const char *target, ! const char *nspname, const char *owner) { ! PGresult *res; ! PQExpBuffer lquery; ! PQExpBuffer cquery; ! PQExpBuffer tgbuf; ! int i_attname; ! int i_provider; ! int i_label; ! int i, ntups; ! /* do nothing, if security label dump is not enabled */ ! if (!security_label) ! return; ! ! /* --data-only skips security labels *except* large object */ ! if (dataOnly && strcmp(type, "LARGE OBJECT") != 0) return; ! /* ! * Fetch security labels associated with the object ! */ ! lquery = createPQExpBuffer(); ! cquery = createPQExpBuffer(); ! tgbuf = createPQExpBuffer(); ! ! appendPQExpBuffer(lquery, ! "SELECT a.attname, s.provider, s.label" ! " FROM pg_catalog.pg_seclabel s" ! " LEFT OUTER JOIN" ! " pg_catalog.pg_attribute a" ! " ON s.objoid = a.attrelid AND" ! " s.objsubid = a.attnum" ! " WHERE classoid = %u AND objoid = %u", ! objCatId.tableoid, objCatId.oid); ! ! res = PQexec(g_conn, lquery->data); ! check_sql_result(res, g_conn, lquery->data, PGRES_TUPLES_OK); ! i_attname = PQfnumber(res, "attname"); ! i_provider = PQfnumber(res, "provider"); ! i_label = PQfnumber(res, "label"); ! ntups = PQntuples(res); ! for (i = 0; i < ntups; i++) { - if (strcmp(type, "TABLE") == 0 && - !PQgetisnull(res, i, i_attname)) - { - appendPQExpBuffer(cquery, - "SECURITY LABEL FOR '%s'" - " ON COLUMN %s.%s IS '%s';\n", - PQgetvalue(res, i, i_provider), - target, - fmtId(PQgetvalue(res, i, i_attname)), - PQgetvalue(res, i, i_label)); - continue; - } - /* ! * a.attname can be available only when type = "TABLE", ! * so we expect it will be always NULL. Elsewhere, we assume ! * it as a data corruption, so simply ignored. */ ! if (!PQgetisnull(res, i, i_attname)) continue; ! appendPQExpBuffer(cquery, ! "SECURITY LABEL FOR '%s' ON %s %s IS '%s';\n", ! PQgetvalue(res, i, i_provider), ! type, target, ! PQgetvalue(res, i, i_label)); } - PQclear(res); ! if (cquery->len > 0) { - appendPQExpBuffer(tgbuf, "%s %s", type, target); ArchiveEntry(fout, nilCatalogId, createDumpId(), ! tgbuf->data, nspname, NULL, owner, false, "SECURITY LABEL", SECTION_NONE, ! cquery->data, "", NULL, ! &(objDumpId), 1, NULL, NULL); } ! destroyPQExpBuffer(lquery); ! destroyPQExpBuffer(cquery); ! destroyPQExpBuffer(tgbuf); } /* --- 10497,10796 ---- /* * dumpSecLabel * ! * This routine is used to dump any security labels associated with the ! * object handed to this routine. The routine takes a constant character ! * string for the target part of the security-label command, plus ! * the namespace and owner of the object (for labeling the ArchiveEntry), ! * plus catalog ID and subid which are the lookup key for pg_seclabel, ! * plus the dump ID for the object (for setting a dependency). * If a matching pg_seclabel entry is found, it is dumped. + * + * Note: although this routine takes a dumpId for dependency purposes, + * that purpose is just to mark the dependency in the emitted dump file + * for possible future use by pg_restore. We do NOT use it for determining + * ordering of the label in the dump file, because this routine is called + * after dependency sorting occurs. This routine should be called just after + * calling ArchiveEntry() for the specified object. */ static void ! dumpSecLabel(Archive *fout, const char *target, ! const char *namespace, const char *owner, ! CatalogId catalogId, int subid, DumpId dumpId) { ! SecLabelItem *labels; ! int nlabels; ! int i; ! PQExpBuffer query; ! /* do nothing, if --no-security-label is supplied */ ! if (no_security_label) return; ! /* Comments are schema not data ... except blob comments are data */ ! if (strncmp(target, "LARGE OBJECT ", 13) != 0) ! { ! if (dataOnly) ! return; ! } ! else ! { ! if (schemaOnly) ! return; ! } ! /* Search for security labels associated with catalogId, using table */ ! nlabels = findSecLabels(fout, catalogId.tableoid, catalogId.oid, &labels); ! query = createPQExpBuffer(); ! for (i = 0; i < nlabels; i++) { /* ! * Ignore label entries which does not match with the supplied subid. ! * However, only relations have objsubid except for zero, and we dump ! * security labels of relations at dumpTableSecLabel(), so it might ! * be a bug if labels[i].objsubid has any values except for zero. */ ! if (labels[i].objsubid != subid) continue; ! appendPQExpBuffer(query, ! "SECURITY LABEL FOR '%s' ON %s IS ", ! labels[i].provider, target); ! appendStringLiteralAH(query, labels[i].label, fout); ! appendPQExpBuffer(query, ";\n"); } ! if (query->len > 0) { ArchiveEntry(fout, nilCatalogId, createDumpId(), ! target, namespace, NULL, owner, false, "SECURITY LABEL", SECTION_NONE, ! query->data, "", NULL, ! &(dumpId), 1, NULL, NULL); } ! destroyPQExpBuffer(query); ! } ! ! /* ! * dumpTableSecLabel ! * ! * As above, but dump security label for both the specified table (or view) ! * and its columns. ! */ ! static void ! dumpTableSecLabel(Archive *fout, TableInfo *tbinfo, const char *reltypename) ! { ! SecLabelItem *labels; ! int nlabels; ! int i; ! PQExpBuffer query; ! PQExpBuffer target; ! ! /* do nothing, if --no-security-label is supplied */ ! if (no_security_label) ! return; ! ! /* SecLabel are SCHEMA not data */ ! if (dataOnly) ! return; ! ! /* Search for comments associated with relation, using table */ ! nlabels = findSecLabels(fout, ! tbinfo->dobj.catId.tableoid, ! tbinfo->dobj.catId.oid, ! &labels); ! ! /* If comments exist, build SECURITY LABEL statements */ ! if (nlabels <= 0) ! return; ! ! query = createPQExpBuffer(); ! target = createPQExpBuffer(); ! ! for (i = 0; i < nlabels; i++) ! { ! const char *colname; ! const char *provider = labels[i].provider; ! const char *label = labels[i].label; ! int objsubid = labels[i].objsubid; ! ! resetPQExpBuffer(target); ! if (objsubid == 0) ! { ! appendPQExpBuffer(target, "%s %s", reltypename, ! fmtId(tbinfo->dobj.name)); ! } ! else ! { ! colname = getAttrName(objsubid, tbinfo); ! appendPQExpBuffer(target, "COLUMN %s.%s", ! fmtId(tbinfo->dobj.name), ! fmtId(colname)); ! } ! appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ", ! provider, target->data); ! appendStringLiteralAH(query, label, fout); ! appendPQExpBuffer(query, ";\n"); ! } ! if (query->len > 0) ! { ! resetPQExpBuffer(target); ! appendPQExpBuffer(target, "%s %s", reltypename, ! fmtId(tbinfo->dobj.name)); ! ArchiveEntry(fout, nilCatalogId, createDumpId(), ! target->data, ! tbinfo->dobj.namespace->dobj.name, ! NULL, tbinfo->rolname, ! false, "SECURITY LABEL", SECTION_NONE, ! query->data, "", NULL, ! &(tbinfo->dobj.dumpId), 1, ! NULL, NULL); ! } ! destroyPQExpBuffer(query); ! destroyPQExpBuffer(target); ! } ! ! /* ! * findSecLabels ! * ! * Find the security label(s), if any, associated with the given object. ! * All the objsubid values associated with the given classoid/objoid are ! * found with one search. ! */ ! static int ! findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items) ! { ! /* static storage for table of security labels */ ! static SecLabelItem *labels = NULL; ! static int nlabels = -1; ! ! SecLabelItem *middle = NULL; ! SecLabelItem *low; ! SecLabelItem *high; ! int nmatch; ! ! /* Get security labels if we didn't already */ ! if (nlabels < 0) ! nlabels = collectSecLabels(fout, &labels); ! ! /* ! * Do binary search to find some item matching the object. ! */ ! low = &labels[0]; ! high = &labels[nlabels - 1]; ! while (low <= high) ! { ! middle = low + (high - low) / 2; ! ! if (classoid < middle->classoid) ! high = middle - 1; ! else if (classoid > middle->classoid) ! low = middle + 1; ! else if (objoid < middle->objoid) ! high = middle - 1; ! else if (objoid > middle->objoid) ! low = middle + 1; ! else ! break; /* found a match */ ! } ! ! if (low > high) /* no matches */ ! { ! *items = NULL; ! return 0; ! } ! ! /* ! * Now determine how many items match the object. The search loop ! * invariant still holds: only items between low and high inclusive could ! * match. ! */ ! nmatch = 1; ! while (middle > low) ! { ! if (classoid != middle[-1].classoid || ! objoid != middle[-1].objoid) ! break; ! middle--; ! nmatch++; ! } ! ! *items = middle; ! ! middle += nmatch; ! while (middle <= high) ! { ! if (classoid != middle->classoid || ! objoid != middle->objoid) ! break; ! middle++; ! nmatch++; ! } ! ! return nmatch; ! } ! ! /* ! * collectSecLabels ! * ! * Construct a table of all security labels available for database objects. ! * We used to do per-object queries for the comments, but it's much faster ! * to pull them all over at once, and on most databases the memory cost ! * isn't high. ! * ! * The table is sorted by classoid/objid/objsubid for speed in lookup. ! */ ! static int ! collectSecLabels(Archive *fout, SecLabelItem **items) ! { ! PGresult *res; ! PQExpBuffer query; ! int i_label; ! int i_provider; ! int i_classoid; ! int i_objoid; ! int i_objsubid; ! int ntups; ! int i; ! SecLabelItem *labels; ! ! query = createPQExpBuffer(); ! ! appendPQExpBuffer(query, ! "SELECT label, provider, classoid, objoid, objsubid " ! "FROM pg_catalog.pg_seclabel " ! "ORDER BY classoid, objoid, objsubid"); ! ! res = PQexec(g_conn, query->data); ! check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); ! ! /* Construct lookup table containing OIDs in numeric form */ ! i_label = PQfnumber(res, "label"); ! i_provider = PQfnumber(res, "provider"); ! i_classoid = PQfnumber(res, "classoid"); ! i_objoid = PQfnumber(res, "objoid"); ! i_objsubid = PQfnumber(res, "objsubid"); ! ! ntups = PQntuples(res); ! ! labels = (SecLabelItem *) malloc(ntups * sizeof(SecLabelItem)); ! ! for (i = 0; i < ntups; i++) ! { ! labels[i].label = PQgetvalue(res, i, i_label); ! labels[i].provider = PQgetvalue(res, i, i_provider); ! labels[i].classoid = atooid(PQgetvalue(res, i, i_classoid)); ! labels[i].objoid = atooid(PQgetvalue(res, i, i_objoid)); ! labels[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid)); ! } ! ! /* Do NOT free the PGresult since we are keeping pointers into it */ ! destroyPQExpBuffer(query); ! ! *items = labels; ! return ntups; } /* *************** *** 10587,10599 **** dumpTable(Archive *fout, TableInfo *tbinfo) tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, tbinfo->relacl); - /* Handle the security label here */ - dumpSecLabel(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, - (tbinfo->relkind == RELKIND_SEQUENCE ? "SEQUENCE" : - (tbinfo->relkind == RELKIND_VIEW ? "VIEW" : "TABLE")), - namecopy, - tbinfo->dobj.namespace->dobj.name, - tbinfo->rolname); /* * Handle column ACLs, if any. Note: we pull these with a separate * query rather than trying to fetch them during getTableAttrs, so --- 10817,10822 ---- *************** *** 11086,11091 **** dumpTableSchema(Archive *fout, TableInfo *tbinfo) --- 11309,11317 ---- /* Dump Table Comments */ dumpTableComment(fout, tbinfo, reltypename); + /* Dump Table Security Labels */ + dumpTableSecLabel(fout, tbinfo, reltypename); + /* Dump comments on inlined table constraints */ for (j = 0; j < tbinfo->ncheck; j++) { *************** *** 11799,11810 **** dumpSequence(Archive *fout, TableInfo *tbinfo) } } ! /* Dump Sequence Comments */ resetPQExpBuffer(query); appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name)); dumpComment(fout, query->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); } if (!schemaOnly) --- 12025,12039 ---- } } ! /* Dump Sequence Comments and Security Labels */ resetPQExpBuffer(query); appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name)); dumpComment(fout, query->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); + dumpSecLabel(fout, query->data, + tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, + tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); } if (!schemaOnly) *** a/src/bin/pg_dump/pg_dumpall.c --- b/src/bin/pg_dump/pg_dumpall.c *************** *** 69,75 **** static int disable_triggers = 0; static int inserts = 0; static int no_tablespaces = 0; static int use_setsessauth = 0; ! static int security_label = 0; static int server_version; static FILE *OPF; --- 69,75 ---- static int inserts = 0; static int no_tablespaces = 0; static int use_setsessauth = 0; ! static int no_security_label = 0; static int server_version; static FILE *OPF; *************** *** 134,140 **** main(int argc, char *argv[]) {"quote-all-identifiers", no_argument, "e_all_identifiers, 1}, {"role", required_argument, NULL, 3}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ! {"security-label", no_argument, &security_label, 1}, {NULL, 0, NULL, 0} }; --- 134,140 ---- {"quote-all-identifiers", no_argument, "e_all_identifiers, 1}, {"role", required_argument, NULL, 3}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ! {"no-security-label", no_argument, &no_security_label, 1}, {NULL, 0, NULL, 0} }; *************** *** 288,295 **** main(int argc, char *argv[]) no_tablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; ! else if (strcmp(optarg, "security-label") == 0) ! security_label = 1; else { fprintf(stderr, --- 288,295 ---- no_tablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; ! else if (strcmp(optarg, "no-security-label") == 0) ! no_security_label = 1; else { fprintf(stderr, *************** *** 375,382 **** main(int argc, char *argv[]) appendPQExpBuffer(pgdumpopts, " --quote-all-identifiers"); if (use_setsessauth) appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); ! if (security_label) ! appendPQExpBuffer(pgdumpopts, " --security-label"); /* * If there was a database specified on the command line, use that, --- 375,382 ---- appendPQExpBuffer(pgdumpopts, " --quote-all-identifiers"); if (use_setsessauth) appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); ! if (no_security_label) ! appendPQExpBuffer(pgdumpopts, " --no-security-label"); /* * If there was a database specified on the command line, use that, *************** *** 573,579 **** help(void) printf(_(" --no-tablespaces do not dump tablespace assignments\n")); printf(_(" --quote-all-identifiers quote all identifiers, even if not keywords\n")); printf(_(" --role=ROLENAME do SET ROLE before dump\n")); ! printf(_(" --security-label also dump security labels\n")); printf(_(" --use-set-session-authorization\n" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); --- 573,579 ---- printf(_(" --no-tablespaces do not dump tablespace assignments\n")); printf(_(" --quote-all-identifiers quote all identifiers, even if not keywords\n")); printf(_(" --role=ROLENAME do SET ROLE before dump\n")); ! printf(_(" --no-security-label do not dump security label assignments\n")); printf(_(" --use-set-session-authorization\n" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); *** a/src/test/regress/GNUmakefile --- b/src/test/regress/GNUmakefile *************** *** 109,115 **** installdirs-tests: installdirs # Get some extra C modules from contrib/spi... ! all: refint$(DLSUFFIX) autoinc$(DLSUFFIX) refint$(DLSUFFIX): $(top_builddir)/contrib/spi/refint$(DLSUFFIX) cp $< $@ --- 109,115 ---- # Get some extra C modules from contrib/spi... ! all: refint$(DLSUFFIX) autoinc$(DLSUFFIX) dummy_esp$(DLSUFFIX) refint$(DLSUFFIX): $(top_builddir)/contrib/spi/refint$(DLSUFFIX) cp $< $@ *************** *** 117,128 **** refint$(DLSUFFIX): $(top_builddir)/contrib/spi/refint$(DLSUFFIX) --- 117,133 ---- autoinc$(DLSUFFIX): $(top_builddir)/contrib/spi/autoinc$(DLSUFFIX) cp $< $@ + dummy_esp$(DLSUFFIX): $(top_builddir)/contrib/dummy_esp/dummy_esp$(DLSUFFIX) + cp $< $@ + $(top_builddir)/contrib/spi/refint$(DLSUFFIX): $(top_srcdir)/contrib/spi/refint.c $(MAKE) -C $(top_builddir)/contrib/spi refint$(DLSUFFIX) $(top_builddir)/contrib/spi/autoinc$(DLSUFFIX): $(top_srcdir)/contrib/spi/autoinc.c $(MAKE) -C $(top_builddir)/contrib/spi autoinc$(DLSUFFIX) + $(top_builddir)/contrib/dummy_esp/dummy_esp$(DLSUFFIX): $(top_builddir)/contrib/dummy_esp/dummy_esp.c + $(MAKE) -C $(top_builddir)/contrib/dummy_esp dummy_esp$(DLSUFFIX) # Tablespace setup *************** *** 171,177 **** bigcheck: all clean distclean maintainer-clean: clean-lib # things built by `all' target ! rm -f $(OBJS) refint$(DLSUFFIX) autoinc$(DLSUFFIX) pg_regress_main.o pg_regress.o pg_regress$(X) # things created by various check targets rm -f $(output_files) $(input_files) rm -rf testtablespace --- 176,183 ---- clean distclean maintainer-clean: clean-lib # things built by `all' target ! rm -f $(OBJS) refint$(DLSUFFIX) autoinc$(DLSUFFIX) dummy_esp$(DLSUFFIX) ! rm -f pg_regress_main.o pg_regress.o pg_regress$(X) # things created by various check targets rm -f $(output_files) $(input_files) rm -rf testtablespace *** a/src/test/regress/expected/rules.out --- b/src/test/regress/expected/rules.out *************** *** 1276,1283 **** drop table cchild; -- Check that ruleutils are working -- SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname; ! viewname | definition ! -----------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); pg_cursors | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin); --- 1276,1283 ---- -- Check that ruleutils are working -- SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname; ! viewname | definition ! -----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); pg_cursors | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin); *************** *** 1287,1292 **** SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem --- 1287,1293 ---- pg_prepared_xacts | SELECT p.transaction, p.gid, p.prepared, u.rolname AS owner, d.datname AS database FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_database d ON ((p.dbid = d.oid))); pg_roles | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, s.setconfig AS rolconfig, pg_authid.oid FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))); pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name); + pg_seclabels | (((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = (SELECT pg_class.oid FROM pg_class WHERE ((pg_class.relname = 'pg_largeobject'::name) AND (pg_class.relnamespace = (SELECT pg_namespace.oid FROM pg_namespace WHERE (pg_namespace.nspname = 'pg_catalog'::name)))))) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0); pg_settings | SELECT a.name, a.setting, a.unit, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val, a.enumvals, a.boot_val, a.reset_val, a.sourcefile, a.sourceline FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline); pg_shadow | SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, pg_authid.rolcreatedb AS usecreatedb, pg_authid.rolsuper AS usesuper, pg_authid.rolcatupdate AS usecatupd, pg_authid.rolpassword AS passwd, (pg_authid.rolvaliduntil)::abstime AS valuntil, s.setconfig AS useconfig FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; pg_stat_activity | SELECT s.datid, d.datname, s.procpid, s.usesysid, u.rolname AS usename, s.application_name, s.client_addr, s.client_port, s.backend_start, s.xact_start, s.query_start, s.waiting, s.current_query FROM pg_database d, pg_stat_get_activity(NULL::integer) s(datid, procpid, usesysid, application_name, current_query, waiting, xact_start, query_start, backend_start, client_addr, client_port), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid)); *************** *** 1333,1339 **** SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color)))); street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp; ! (55 rows) SELECT tablename, rulename, definition FROM pg_rules ORDER BY tablename, rulename; --- 1334,1340 ---- shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color)))); street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp; ! (56 rows) SELECT tablename, rulename, definition FROM pg_rules ORDER BY tablename, rulename; *** /dev/null --- b/src/test/regress/input/security_label.source *************** *** 0 **** --- 1,66 ---- + -- + -- Test for facilities of security label + -- + + -- initial setups + SET client_min_messages TO 'warning'; + + DROP ROLE IF EXISTS esp_user1; + DROP ROLE IF EXISTS esp_user2; + + DROP TABLE IF EXISTS esp_tbl1; + DROP TABLE IF EXISTS esp_tbl2; + DROP TABLE IF EXISTS esp_tbl3; + + CREATE USER esp_user1; + CREATE USER esp_user2; + + CREATE TABLE esp_tbl1 (a int, b text); + CREATE TABLE esp_tbl2 (x int, y text); + + ALTER TABLE esp_tbl1 OWNER TO esp_user1; + ALTER TABLE esp_tbl2 OWNER TO esp_user2; + + RESET client_min_messages; + + -- + -- Test of SECURITY LABEL statement without a plugin + -- + SECURITY LABEL ON TABLE esp_tbl1 IS 'classified'; -- fail + SECURITY LABEL FOR 'dummy' ON TABLE esp_tbl1 IS 'classified'; -- fail + SECURITY LABEL ON TABLE esp_tbl1 IS '...invalid label...'; -- fail + SECURITY LABEL ON TABLE esp_tbl3 IS 'unclassified'; -- fail + + -- Load dummy external security provider + LOAD '@abs_builddir@/dummy_esp'; + + -- + -- Test of SECURITY LABEL statement with a plugin + -- + SET SESSION AUTHORIZATION esp_user1; + + SECURITY LABEL ON TABLE esp_tbl1 IS 'classified'; -- OK + SECURITY LABEL ON COLUMN esp_tbl1.a IS 'unclassified'; -- OK + SECURITY LABEL ON TABLE esp_tbl1 IS '...invalid label...'; -- fail + SECURITY LABEL FOR 'dummy' ON TABLE esp_tbl1 IS 'unclassified'; -- OK + SECURITY LABEL FOR 'unknown_esp' ON TABLE esp_tbl1 IS 'classified'; -- fail + SECURITY LABEL ON TABLE esp_tbl2 IS 'unclassified'; -- fail (not owner) + SECURITY LABEL ON TABLE esp_tbl1 IS 'secret'; -- fail (not superuser) + SECURITY LABEL ON TABLE esp_tbl3 IS 'unclassified'; -- fail (not found) + + SET SESSION AUTHORIZATION esp_user2; + SECURITY LABEL ON TABLE esp_tbl1 IS 'unclassified'; -- fail + SECURITY LABEL ON TABLE esp_tbl2 IS 'classified'; -- OK + + RESET SESSION AUTHORIZATION; + + SECURITY LABEL ON TABLE esp_tbl1 IS 'top secret'; -- OK + + SELECT objtype, objname, provider, label FROM pg_seclabels + WHERE objname in ('esp_tbl1', 'esp_tbl2'); + + -- clean up objects + DROP TABLE esp_tbl1; + DROP TABLE esp_tbl2; + DROP USER esp_user1; + DROP USER esp_user2; *** /dev/null --- b/src/test/regress/output/security_label.source *************** *** 0 **** --- 1,66 ---- + -- + -- Test for facilities of security label + -- + -- initial setups + SET client_min_messages TO 'warning'; + DROP ROLE IF EXISTS esp_user1; + DROP ROLE IF EXISTS esp_user2; + DROP TABLE IF EXISTS esp_tbl1; + DROP TABLE IF EXISTS esp_tbl2; + DROP TABLE IF EXISTS esp_tbl3; + CREATE USER esp_user1; + CREATE USER esp_user2; + CREATE TABLE esp_tbl1 (a int, b text); + CREATE TABLE esp_tbl2 (x int, y text); + ALTER TABLE esp_tbl1 OWNER TO esp_user1; + ALTER TABLE esp_tbl2 OWNER TO esp_user2; + RESET client_min_messages; + -- + -- Test of SECURITY LABEL statement without a plugin + -- + SECURITY LABEL ON TABLE esp_tbl1 IS 'classified'; -- fail + ERROR: security label providers have been loaded + SECURITY LABEL FOR 'dummy' ON TABLE esp_tbl1 IS 'classified'; -- fail + ERROR: security label provider "dummy" is not loaded + SECURITY LABEL ON TABLE esp_tbl1 IS '...invalid label...'; -- fail + ERROR: security label providers have been loaded + SECURITY LABEL ON TABLE esp_tbl3 IS 'unclassified'; -- fail + ERROR: security label providers have been loaded + -- Load dummy external security provider + LOAD '@abs_builddir@/dummy_esp'; + -- + -- Test of SECURITY LABEL statement with a plugin + -- + SET SESSION AUTHORIZATION esp_user1; + SECURITY LABEL ON TABLE esp_tbl1 IS 'classified'; -- OK + SECURITY LABEL ON COLUMN esp_tbl1.a IS 'unclassified'; -- OK + SECURITY LABEL ON TABLE esp_tbl1 IS '...invalid label...'; -- fail + ERROR: '...invalid label...' is not a valid security label + SECURITY LABEL FOR 'dummy' ON TABLE esp_tbl1 IS 'unclassified'; -- OK + SECURITY LABEL FOR 'unknown_esp' ON TABLE esp_tbl1 IS 'classified'; -- fail + ERROR: security label provider "unknown_esp" is not loaded + SECURITY LABEL ON TABLE esp_tbl2 IS 'unclassified'; -- fail (not owner) + ERROR: must be owner of relation esp_tbl2 + SECURITY LABEL ON TABLE esp_tbl1 IS 'secret'; -- fail (not superuser) + ERROR: only superuser can set 'secret' label + SECURITY LABEL ON TABLE esp_tbl3 IS 'unclassified'; -- fail (not found) + ERROR: relation "esp_tbl3" does not exist + SET SESSION AUTHORIZATION esp_user2; + SECURITY LABEL ON TABLE esp_tbl1 IS 'unclassified'; -- fail + ERROR: must be owner of relation esp_tbl1 + SECURITY LABEL ON TABLE esp_tbl2 IS 'classified'; -- OK + RESET SESSION AUTHORIZATION; + SECURITY LABEL ON TABLE esp_tbl1 IS 'top secret'; -- OK + SELECT objtype, objname, provider, label FROM pg_seclabels + WHERE objname in ('esp_tbl1', 'esp_tbl2'); + objtype | objname | provider | label + ---------+----------+----------+------------ + table | esp_tbl1 | dummy | top secret + table | esp_tbl2 | dummy | classified + (2 rows) + + -- clean up objects + DROP TABLE esp_tbl1; + DROP TABLE esp_tbl2; + DROP USER esp_user1; + DROP USER esp_user2; *** a/src/test/regress/parallel_schedule --- b/src/test/regress/parallel_schedule *************** *** 76,82 **** ignore: random # ---------- test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join aggregates transactions random portals arrays btree_index hash_index update namespace prepared_xacts delete ! test: privileges test: misc # rules cannot run concurrently with any test that creates a view test: rules --- 76,82 ---- # ---------- test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join aggregates transactions random portals arrays btree_index hash_index update namespace prepared_xacts delete ! test: privileges security_label test: misc # rules cannot run concurrently with any test that creates a view test: rules *** a/src/test/regress/serial_schedule --- b/src/test/regress/serial_schedule *************** *** 88,93 **** test: delete --- 88,94 ---- test: namespace test: prepared_xacts test: privileges + test: security_label test: misc test: rules test: select_views