GPDB compatibility fixes:

When connected to a Greenplum database server
- Hide items that not work with GPDB, such as Triggers, FDW, FTS_*, etc
- Tables/Views/Catalogs/Language node can expand correctly
- Views/Languages/Catalogs can show properties dialog with correct information
- Show a greenplum icon at the server node

Teng Zhang & Hao Wang
This commit is contained in:
Teng Zhang 2017-08-30 11:18:14 +01:00 committed by Dave Page
parent 048205ead5
commit 821442ed8f
54 changed files with 938 additions and 19 deletions

View File

@ -53,6 +53,7 @@ class CastModule(CollectionNodeModule):
def __init__(self, *args, **kwargs):
super(CastModule, self).__init__(*args, **kwargs)
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did):
"""

View File

@ -60,6 +60,7 @@ class EventTriggerModule(CollectionNodeModule):
super(EventTriggerModule, self).__init__(*args, **kwargs)
self.min_ver = 90300
self.max_ver = None
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did):
"""

View File

@ -60,6 +60,7 @@ class ExtensionModule(CollectionNodeModule):
Initialising the base class
"""
super(ExtensionModule, self).__init__(*args, **kwargs)
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did):
"""

View File

@ -64,6 +64,7 @@ class ForeignDataWrapperModule(CollectionNodeModule):
self.max_ver = None
super(ForeignDataWrapperModule, self).__init__(*args, **kwargs)
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did):
"""

View File

@ -223,7 +223,9 @@ class LanguageView(PGChildNodeView):
self.manager = self.driver.connection_manager(kwargs['sid'])
self.conn = self.manager.connection(did=kwargs['did'])
# Set the template path for the SQL scripts
self.template_path = "languages/sql/#{0}#".format(self.manager.version)
self.template_path = ("languages/sql/#gpdb#{0}#".format(self.manager.version)
if self.manager.server_type == 'gpdb'
else "languages/sql/#{0}#".format(self.manager.version))
return f(*args, **kwargs)

View File

@ -0,0 +1,34 @@
SELECT 'lanacl' as deftype, COALESCE(gt.rolname, 'PUBLIC') grantee, g.rolname grantor,
array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
FROM
(SELECT
d.grantee, d.grantor, d.is_grantable,
CASE d.privilege_type
WHEN 'USAGE' THEN 'U'
ELSE 'UNKNOWN'
END AS privilege_type
FROM
(SELECT lanacl FROM pg_language lan
LEFT OUTER JOIN pg_shdescription descr ON (lan.oid=descr.objoid AND descr.classoid='pg_language'::regclass)
WHERE lan.oid = {{ lid|qtLiteral }}::OID
) acl,
(SELECT
u_grantor.oid AS grantor,
grantee.oid AS grantee,
pr.type AS privilege_type,
aclcontains(lan1.lanacl, makeaclitem(grantee.oid, u_grantor.oid, pr.type, true)) AS is_grantable
FROM pg_language lan1, pg_authid u_grantor, (
SELECT pg_authid.oid, pg_authid.rolname
FROM pg_authid
UNION ALL
SELECT 0::oid AS oid, 'PUBLIC') grantee(oid, rolname),
(SELECT 'USAGE') pr(type)
WHERE aclcontains(lan1.lanacl, makeaclitem(grantee.oid, u_grantor.oid, pr.type, false))
AND (pg_has_role(u_grantor.oid, 'USAGE'::text) OR pg_has_role(grantee.oid, 'USAGE'::text)
OR grantee.rolname = 'PUBLIC'::name)
AND lan1.oid = {{ lid|qtLiteral }}::OID
) d
) d
LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
GROUP BY g.rolname, gt.rolname

View File

@ -0,0 +1,22 @@
SELECT
lan.oid as oid, lanname as name, lanpltrusted as trusted,
array_to_string(lanacl::text[], ', ') as acl, hp.proname as lanproc,
vp.proname as lanval, description,
pg_get_userbyid(lan.lanowner) as lanowner, ip.proname as laninl
FROM
pg_language lan JOIN pg_proc hp ON hp.oid=lanplcallfoid
LEFT OUTER JOIN pg_proc ip ON ip.oid=laninline
LEFT OUTER JOIN pg_proc vp ON vp.oid=lanvalidator
LEFT OUTER JOIN pg_description des
ON (
des.objoid=lan.oid AND des.objsubid=0 AND
des.classoid='pg_language'::regclass
)
WHERE lanispl IS TRUE
{% if lid %} AND
lan.oid={{lid}}::oid
{% endif %}
{% if lanname %} AND
lanname={{ lanname|qtLiteral }}::text
{% endif %}
ORDER BY lanname

View File

@ -139,11 +139,13 @@ def check_precondition(f):
self.conn = self.manager.connection(did=kwargs['did'])
# Set the template path for the SQL scripts
self.template_path = self.template_initial + '/' + (
self.ppas_template_path(self.manager.version)
if self.manager.server_type == 'ppas' else
self.pg_template_path(self.manager.version)
)
if self.manager.server_type == 'gpdb':
_temp = self.gpdb_template_path(self.manager.version)
elif self.manager.server_type == 'ppas':
_temp = self.ppas_template_path(self.manager.version)
else:
_temp = self.pg_template_path(self.manager.version)
self.template_path = self.template_initial + '/' + _temp
return f(*args, **kwargs)
@ -250,6 +252,13 @@ class SchemaView(PGChildNodeView):
"""
return 'pg/#{0}#'.format(ver)
@staticmethod
def gpdb_template_path(ver):
"""
Returns the template path for GreenPlum servers.
"""
return '#gpdb#{0}#'.format(ver)
def format_request_acls(self, data, modified=False, specific=None):
acls = {}
try:

View File

@ -65,6 +65,7 @@ class CollationModule(SchemaChildModule):
super(CollationModule, self).__init__(*args, **kwargs)
self.min_ver = 90100
self.max_ver = None
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did, scid):
"""

View File

@ -56,6 +56,7 @@ class DomainModule(SchemaChildModule):
super(DomainModule, self).__init__(*args, **kwargs)
self.min_ver = None
self.max_ver = None
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did, scid):
"""

View File

@ -63,6 +63,7 @@ class ForeignTableModule(SchemaChildModule):
super(ForeignTableModule, self).__init__(*args, **kwargs)
self.min_ver = None
self.max_ver = None
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did, scid):
"""

View File

@ -59,6 +59,7 @@ class FtsConfigurationModule(SchemaChildModule):
self.max_ver = None
self.manager = None
super(FtsConfigurationModule, self).__init__(*args, **kwargs)
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did, scid):
"""

View File

@ -58,6 +58,7 @@ class FtsDictionaryModule(SchemaChildModule):
self.max_ver = None
self.manager = None
super(FtsDictionaryModule, self).__init__(*args, **kwargs)
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did, scid):
"""

View File

@ -48,6 +48,10 @@ class FtsParserModule(SchemaChildModule):
NODE_TYPE = 'fts_parser'
COLLECTION_LABEL = _('FTS Parsers')
def __init__(self, *args, **kwargs):
super(FtsParserModule, self).__init__(*args, **kwargs)
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did, scid):
"""
Generate the collection node

View File

@ -55,6 +55,7 @@ class FtsTemplateModule(SchemaChildModule):
self.min_ver = None
self.max_ver = None
super(FtsTemplateModule, self).__init__(*args, **kwargs)
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did, scid):
"""

View File

@ -74,6 +74,7 @@ class FunctionModule(SchemaChildModule):
self.min_ver = 90100
self.max_ver = None
self.server_type = None
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did, scid):
"""
@ -1550,6 +1551,7 @@ class ProcedureModule(SchemaChildModule):
self.min_ver = 90100
self.max_ver = None
self.min_gpdbver = 1000000000
self.server_type = ['ppas']
def get_nodes(self, gid, sid, did, scid):
@ -1661,6 +1663,7 @@ class TriggerFunctionModule(SchemaChildModule):
self.min_ver = 90100
self.max_ver = None
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did, scid):
"""

View File

@ -60,6 +60,7 @@ class SequenceModule(SchemaChildModule):
super(SequenceModule, self).__init__(*args, **kwargs)
self.min_ver = None
self.max_ver = None
self.min_gpdbver = 1000000000
def get_nodes(self, gid, sid, did, scid):
"""

View File

@ -88,6 +88,8 @@ class PartitionsModule(CollectionNodeModule):
"""
Load this module if it is a partition table
"""
if manager.server_type == 'gpdb':
return False
if 'tid' in kwargs and CollectionNodeModule.BackendSupported(self, manager, **kwargs):
conn = manager.connection(did=kwargs['did'])

View File

@ -62,6 +62,7 @@ class TriggerModule(CollectionNodeModule):
"""
self.min_ver = None
self.max_ver = None
self.min_gpdbver = 1000000000
super(TriggerModule, self).__init__(*args, **kwargs)
def BackendSupported(self, manager, **kwargs):
@ -69,6 +70,8 @@ class TriggerModule(CollectionNodeModule):
Load this module if vid is view, we will not load it under
material view
"""
if manager.server_type == 'gpdb':
return False
if super(TriggerModule, self).BackendSupported(manager, **kwargs):
conn = manager.connection(did=kwargs['did'])

View File

@ -0,0 +1,21 @@
{% macro LIST(tbl) -%}
({{ tbl }}.nspname = 'pg_catalog' AND EXISTS
(SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND
relnamespace = {{ tbl }}.oid LIMIT 1)) OR
({{ tbl }}.nspname = 'information_schema' AND EXISTS
(SELECT 1 FROM pg_class WHERE relname = 'tables' AND
relnamespace = {{ tbl }}.oid LIMIT 1))
{%- endmacro %}
{% macro LABELS(tbl, _) -%}
CASE {{ tbl }}.nspname
WHEN 'pg_catalog' THEN '{{ _( 'PostgreSQL Catalog' ) }} (pg_catalog)'
WHEN 'information_schema' THEN '{{ _( 'ANSI' ) }} (information_schema)'
ELSE {{ tbl }}.nspname
END AS name
{%- endmacro %}
{% macro DB_SUPPORT(tbl) -%}
CASE
WHEN {{ tbl }}.nspname = ANY('{information_schema}')
THEN false
ELSE true END
{%- endmacro %}

View File

@ -0,0 +1,38 @@
{# Fetch privileges for schema #}
SELECT
'nspacl' as deftype, COALESCE(gt.rolname, 'PUBLIC') AS grantee,
g.rolname AS grantor, array_agg(b.privilege_type) AS privileges,
array_agg(b.is_grantable) AS grantable
FROM
(SELECT
(d).grantee AS grantee, (d).grantor AS grantor,
(d).is_grantable AS is_grantable,
CASE (d).privilege_type
WHEN 'CREATE' THEN 'C'
WHEN 'USAGE' THEN 'U'
ELSE 'UNKNOWN - ' || (d).privilege_type
END AS privilege_type
FROM
(
SELECT
u_grantor.oid AS grantor,
grantee.oid AS grantee,
pr.type AS privilege_type,
aclcontains(nc.nspacl, makeaclitem(grantee.oid, u_grantor.oid, pr.type, true)) AS is_grantable
FROM pg_namespace nc, pg_authid u_grantor, (
SELECT pg_authid.oid, pg_authid.rolname
FROM pg_authid
UNION ALL
SELECT 0::oid AS oid, 'PUBLIC') grantee(oid, rolname),
( SELECT 'CREATE'
UNION ALL
SELECT 'USAGE') pr(type)
WHERE aclcontains(nc.nspacl, makeaclitem(grantee.oid, u_grantor.oid, pr.type, false))
AND (pg_has_role(u_grantor.oid, 'USAGE'::text) OR pg_has_role(grantee.oid, 'USAGE'::text)
OR grantee.rolname = 'PUBLIC'::name)
AND nsp.oid = {{ scid|qtLiteral }}::OID
) a
) b
LEFT JOIN pg_catalog.pg_roles g ON (b.grantor = g.oid)
LEFT JOIN pg_catalog.pg_roles gt ON (b.grantee = gt.oid)
GROUP BY g.rolname, gt.rolname;

View File

@ -0,0 +1,17 @@
{% import 'macros/privilege.macros' as PRIVILEGE %}
{% if data %}
CREATE SCHEMA {{ conn|qtIdent(data.name) }}
{% if data.namespaceowner %}
AUTHORIZATION {{ conn|qtIdent(data.namespaceowner) }};
{% endif %}
{% if data.description %}
COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }}
IS {{ data.description|qtLiteral }};
{% endif %}
{% if data.nspacl %}
{% for priv in data.nspacl %}
{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %}
{% endif %}
{% endif %}

View File

@ -0,0 +1 @@
SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{ scid|qtLiteral }};

View File

@ -0,0 +1,9 @@
{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %}
SELECT
nsp.nspname as schema_name,
{{ CATALOGS.LIST('nsp') }} AS is_catalog,
{{ CATALOGS.DB_SUPPORT('nsp') }} AS db_support
FROM
pg_catalog.pg_namespace nsp
WHERE
nsp.oid = {{ scid|qtLiteral }}::OID;

View File

@ -0,0 +1,16 @@
{% import 'catalog/gpdb_5.0_plus/macros/catalogs.sql' as CATALOGS %}
SELECT
nsp.oid,
{{ CATALOGS.LABELS('nsp', _) }},
has_schema_privilege(nsp.oid, 'CREATE') as can_create,
has_schema_privilege(nsp.oid, 'USAGE') as has_usage
FROM
pg_namespace nsp
WHERE
{% if scid %}
nsp.oid={{scid}}::oid AND
{% endif %}
(
{{ CATALOGS.LIST('nsp') }}
)
ORDER BY 2;

View File

@ -0,0 +1,24 @@
{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %}
SELECT
2 AS nsptyp,
nsp.nspname AS name,
nsp.oid,
array_to_string(nsp.nspacl::text[], ', ') as acl,
r.rolname AS namespaceowner, description,
has_schema_privilege(nsp.oid, 'CREATE') AS can_create,
CASE
WHEN nspname LIKE E'pg\\_%' THEN true
ELSE false END AS is_sys_object
FROM
pg_namespace nsp
LEFT OUTER JOIN pg_description des ON
(des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
LEFT JOIN pg_roles r ON (r.oid = nsp.nspowner)
WHERE
{% if scid %}
nsp.oid={{scid}}::oid AND
{% endif %}
(
{{ CATALOGS.LIST('nsp') }}
)
ORDER BY 1, nspname;

View File

@ -0,0 +1,30 @@
{% import 'macros/security.macros' as SECLABEL %}
{% if data %}
{# ==== To update catalog comments ==== #}
{% if data.description and data.description != o_data.description %}
COMMENT ON SCHEMA {{ conn|qtIdent(o_data.name) }}
IS {{ data.description|qtLiteral }};
{% endif %}
{# ==== To update catalog securitylabel ==== #}
{# The SQL generated below will change Security Label #}
{% if data.seclabels and data.seclabels|length > 0 %}
{% set seclabels = data.seclabels %}
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
{% for r in seclabels.deleted %}
{{ SECLABEL.DROP(conn, 'SCHEMA', o_data.name, r.provider) }}
{% endfor %}
{% endif %}
{% if 'added' in seclabels and seclabels.added|length > 0 %}
{% for r in seclabels.added %}
{{ SECLABEL.APPLY(conn, 'SCHEMA', o_data.name, r.provider, r.label) }}
{% endfor %}
{% endif %}
{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
{% for r in seclabels.changed %}
{{ SECLABEL.APPLY(conn, 'SCHEMA', o_data.name, r.provider, r.label) }}
{% endfor %}
{% endif %}
{% endif %}
{% endif %}

View File

@ -0,0 +1,30 @@
{# List of allowed privileges for PostgreSQL 9.2 or later #}
{#
Format for allowed privileges is:
"acl_col": {
"type": "name",
"acl": [...]
}
#}
{
"nspacl": {
"type": "SCHEMA",
"acl": ["C", "U"]
},
"deftblacl": {
"type": "TABLE",
"acl": ["r", "a", "w", "d", "D", "x", "t"]
},
"defseqacl": {
"type": "SEQUENCE",
"acl": ["U", "r", "a"]
},
"deffuncacl": {
"type": "FUNCTION",
"acl": ["X"]
},
"deftypeacl": {
"type": "TYPE",
"acl": ["U"]
}
}

View File

@ -0,0 +1,23 @@
{# Fetch privileges for schema #}
SELECT
'nspacl' as deftype, COALESCE(gt.rolname, 'PUBLIC') AS grantee,
g.rolname AS grantor, array_agg(b.privilege_type) AS privileges,
array_agg(b.is_grantable) AS grantable
FROM
(SELECT
(d).grantee AS grantee, (d).grantor AS grantor,
(d).is_grantable AS is_grantable,
CASE (d).privilege_type
WHEN 'CREATE' THEN 'C'
WHEN 'USAGE' THEN 'U'
ELSE 'UNKNOWN - ' || (d).privilege_type
END AS privilege_type
FROM
(SELECT aclexplode(nsp.nspacl) as d
FROM pg_namespace nsp
WHERE nsp.oid = {{ scid|qtLiteral }}::OID
) a
) b
LEFT JOIN pg_catalog.pg_roles g ON (b.grantor = g.oid)
LEFT JOIN pg_catalog.pg_roles gt ON (b.grantee = gt.oid)
GROUP BY g.rolname, gt.rolname;

View File

@ -0,0 +1,40 @@
{% import 'macros/security.macros' as SECLABEL %}
{% import 'macros/privilege.macros' as PRIVILEGE %}
{% import 'macros/default_privilege.macros' as DEFAULT_PRIVILEGE %}
{% if data.name %}
CREATE SCHEMA {{ conn|qtIdent(data.name) }}{% if data.namespaceowner %}
AUTHORIZATION {{ conn|qtIdent(data.namespaceowner) }}{% endif %}{% endif %};
{# Alter the comment/description #}
{% if data.description %}
COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }}
IS {{ data.description|qtLiteral }};
{% endif %}
{# ACL for the schema #}
{% if data.nspacl %}
{% for priv in data.nspacl %}
{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %}
{% endif %}
{# Default privileges on tables #}
{% for defacl, type in [
('deftblacl', 'TABLES'), ('defseqacl', 'SEQUENCES'),
('deffuncacl', 'FUNCTIONS'), ('deftypeacl', 'TYPES')]
%}
{% if data[defacl] %}{% set acl = data[defacl] %}
{% for priv in acl %}
{{ DEFAULT_PRIVILEGE.SET(
conn, 'SCHEMA', data.name, type, priv.grantee,
priv.without_grant, priv.with_grant
) }}{% endfor %}
{% endif %}
{% endfor %}
{# Security Labels on schema #}
{% if data.seclabels and data.seclabels|length > 0 %}
{% for r in data.seclabels %}
{{ SECLABEL.APPLY(conn, 'SCHEMA', data.name, r.provider, r.label) }}
{% endfor %}
{% endif %}

View File

@ -0,0 +1,40 @@
SELECT
CASE (a.deftype)
WHEN 'r' THEN 'deftblacl'
WHEN 'S' THEN 'defseqacl'
WHEN 'f' THEN 'deffuncacl'
WHEN 'T' THEN 'deftypeacl'
ELSE 'UNKNOWN - ' || a.deftype
END AS deftype,
COALESCE(gt.rolname, 'PUBLIC') grantee, g.rolname grantor, array_agg(a.privilege_type) as privileges, array_agg(a.is_grantable) as grantable
FROM
(SELECT
(acl).grantee as grantee, (acl).grantor AS grantor, (acl).is_grantable AS is_grantable,
CASE (acl).privilege_type
WHEN 'CONNECT' THEN 'c'
WHEN 'CREATE' THEN 'C'
WHEN 'DELETE' THEN 'd'
WHEN 'EXECUTE' THEN 'X'
WHEN 'INSERT' THEN 'a'
WHEN 'REFERENCES' THEN 'x'
WHEN 'SELECT' THEN 'r'
WHEN 'TEMPORARY' THEN 'T'
WHEN 'TRIGGER' THEN 't'
WHEN 'TRUNCATE' THEN 'D'
WHEN 'UPDATE' THEN 'w'
WHEN 'USAGE' THEN 'U'
ELSE 'UNKNOWN - ' || (acl).privilege_type
END AS privilege_type,
defaclobjtype as deftype
FROM
(SELECT defaclobjtype, aclexplode(defaclacl) as acl
FROM
pg_namespace nsp
LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid)
WHERE
nsp.oid={{scid}}::oid
) d) a
LEFT JOIN pg_catalog.pg_roles g ON (a.grantor = g.oid)
LEFT JOIN pg_catalog.pg_roles gt ON (a.grantee = gt.oid)
GROUP BY g.rolname, gt.rolname, a.deftype
ORDER BY a.deftype;

View File

@ -0,0 +1 @@
DROP SCHEMA {{ conn|qtIdent(name) }} {% if cascade %}CASCADE{%endif%};

View File

@ -0,0 +1 @@
SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{ scid|qtLiteral }};

View File

@ -0,0 +1,9 @@
{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %}
SELECT
nsp.nspname as schema_name,
{{ CATALOGS.LIST('nsp') }} AS is_catalog,
{{ CATALOGS.DB_SUPPORT('nsp') }} AS db_support
FROM
pg_catalog.pg_namespace nsp
WHERE
nsp.oid = {{ scid|qtLiteral }}::OID;

View File

@ -0,0 +1,20 @@
{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %}
SELECT
nsp.oid,
nsp.nspname as name,
has_schema_privilege(nsp.oid, 'CREATE') as can_create,
has_schema_privilege(nsp.oid, 'USAGE') as has_usage
FROM
pg_namespace nsp
WHERE
{% if scid %}
nsp.oid={{scid}}::oid AND
{% else %}
{% if not show_sysobj %}
nspname NOT LIKE E'pg\_%' AND
{% endif %}
{% endif %}
NOT (
{{ CATALOGS.LIST('nsp') }}
)
ORDER BY nspname;

View File

@ -0,0 +1 @@
SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = {{ schema|qtLiteral }};

View File

@ -0,0 +1,57 @@
{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %}
SELECT
CASE
WHEN (nspname LIKE E'pg\\_temp\\_%') THEN 1
WHEN (nspname LIKE E'pg\\_%') THEN 0
ELSE 3 END AS nsptyp,
nsp.nspname AS name,
nsp.oid,
array_to_string(nsp.nspacl::text[], ', ') as acl,
r.rolname AS namespaceowner, description,
has_schema_privilege(nsp.oid, 'CREATE') AS can_create,
CASE
WHEN nspname LIKE E'pg\\_%' THEN true
ELSE false END AS is_sys_object,
{### Default ACL for Tables ###}
(SELECT array_to_string(ARRAY(
SELECT array_to_string(defaclacl::text[], ', ')
FROM pg_default_acl
WHERE defaclobjtype = 'r' AND defaclnamespace = nsp.oid
), ', ')) AS tblacl,
{### Default ACL for Sequnces ###}
(SELECT array_to_string(ARRAY(
SELECT array_to_string(defaclacl::text[], ', ')
FROM pg_default_acl
WHERE defaclobjtype = 'S' AND defaclnamespace = nsp.oid
), ', ')) AS seqacl,
{### Default ACL for Functions ###}
(SELECT array_to_string(ARRAY(
SELECT array_to_string(defaclacl::text[], ', ')
FROM pg_default_acl
WHERE defaclobjtype = 'f' AND defaclnamespace = nsp.oid
), ', ')) AS funcacl,
{### Default ACL for Type ###}
(SELECT array_to_string(ARRAY(
SELECT array_to_string(defaclacl::text[], ', ')
FROM pg_default_acl
WHERE defaclobjtype = 'T' AND defaclnamespace = nsp.oid
), ', ')) AS typeacl,
(SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=nsp.oid) AS seclabels
FROM
pg_namespace nsp
LEFT OUTER JOIN pg_description des ON
(des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
LEFT JOIN pg_roles r ON (r.oid = nsp.nspowner)
WHERE
{% if scid %}
nsp.oid={{scid}}::oid AND
{% else %}
{% if show_sysobj %}
nspname NOT LIKE E'pg\\_temp\\_%' AND
nspname NOT LIKE E'pg\\_toast\\_temp\\_%' AND
{% endif %}
{% endif %}
NOT (
{{ CATALOGS.LIST('nsp') }}
)
ORDER BY 1, nspname;

View File

@ -0,0 +1,83 @@
{% import 'macros/security.macros' as SECLABEL %}
{% import 'macros/privilege.macros' as PRIVILEGE %}
{% import 'macros/default_privilege.macros' as DEFAULT_PRIVILEGE %}
{# Rename the schema #}
{% if data.name and data.name != o_data.name %}
ALTER SCHEMA {{ conn|qtIdent(o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}
{# Change the owner #}
{% if data.namespaceowner and data.namespaceowner != o_data.namespaceowner %}
ALTER SCHEMA {{ conn|qtIdent(data.name) }}
OWNER TO {{ conn|qtIdent(data.namespaceowner) }};
{% endif %}
{# Update the comments/description #}
{% if data.description is defined and data.description != o_data.description %}
COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }}
IS {{ data.description|qtLiteral }};
{% endif %}
{# Change the privileges #}
{% if data.nspacl %}
{% if 'deleted' in data.nspacl %}
{% for priv in data.nspacl.deleted %}
{{ PRIVILEGE.RESETALL(conn, 'SCHEMA', priv.grantee, data.name) }}
{% endfor %}
{% endif %}
{% if 'changed' in data.nspacl %}
{% for priv in data.nspacl.changed %}
{{ PRIVILEGE.RESETALL(conn, 'SCHEMA', priv.grantee, data.name) }}
{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
{% endfor %}
{% endif %}
{% if 'added' in data.nspacl %}
{% for priv in data.nspacl.added %}
{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
{% endfor %}
{% endif %}
{% endif %}
{# Change the default privileges #}
{% for defacl, type in [
('deftblacl', 'TABLES'), ('defseqacl', 'SEQUENCES'),
('deffuncacl', 'FUNCTIONS'), ('deftypeacl', 'TYPES')]
%}
{% if data[defacl] %}{% set acl = data[defacl] %}
{% if 'deleted' in acl %}
{% for priv in acl.deleted %}
{{ DEFAULT_PRIVILEGE.UNSET(conn, 'SCHEMA', data.name, type, priv.grantee) }}
{% endfor %}
{% endif %}
{% if 'changed' in acl %}
{% for priv in acl.changed %}
{{ DEFAULT_PRIVILEGE.UNSET(conn, 'SCHEMA', data.name, type, priv.grantee) }}
{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, type, priv.grantee, priv.without_grant, priv.with_grant) }}
{% endfor %}
{% endif %}
{% if 'added' in acl %}
{% for priv in acl.added %}
{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, type, priv.grantee, priv.without_grant, priv.with_grant) }}
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
{# Change the security labels #}
{% if data.seclabels and data.seclabels|length > 0 %}
{% set seclabels = data.seclabels %}
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
{% for r in seclabels.deleted %}
{{ SECLABEL.DROP(conn, 'SCHEMA', data.name, r.provider) }}
{% endfor %}
{% endif %}
{% if 'added' in seclabels and seclabels.added|length > 0 %}
{% for r in seclabels.added %}
{{ SECLABEL.APPLY(conn, 'SCHEMA', data.name, r.provider, r.label) }}
{% endfor %}
{% endif %}
{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
{% for r in seclabels.changed %}
{{ SECLABEL.APPLY(conn, 'SCHEMA', data.name, r.provider, r.label) }}
{% endfor %}
{% endif %}
{% endif %}

View File

@ -143,6 +143,7 @@ class MViewModule(ViewModule):
super(MViewModule, self).__init__(*args, **kwargs)
self.min_ver = 90300
self.max_ver = None
self.min_gpdbver = 1000000000
view_blueprint = ViewModule(__name__)
@ -176,11 +177,13 @@ def check_precondition(f):
kwargs['did'] in self.manager.db_info else 0
# Set template path for sql scripts
self.template_path = self.template_initial + '/' + (
self.ppas_template_path(self.manager.version)
if self.manager.server_type == 'ppas' else
self.pg_template_path(self.manager.version)
)
if self.manager.server_type == 'gpdb':
_temp = self.gpdb_template_path(self.manager.version)
elif self.manager.server_type == 'ppas':
_temp = self.ppas_template_path(self.manager.version)
else:
_temp = self.pg_template_path(self.manager.version)
self.template_path = self.template_initial + '/' + _temp
self.column_template_path = 'column/sql/#{0}#'.format(self.manager.version)
@ -313,6 +316,13 @@ class ViewNode(PGChildNodeView, VacuumSettings):
"""
return 'pg/#{0}#'.format(ver)
@staticmethod
def gpdb_template_path(ver):
"""
Returns the template path for GreenPlum servers.
"""
return '#gpdb#{0}#'.format(ver)
@check_precondition
def list(self, gid, sid, did, scid):
"""
@ -859,6 +869,9 @@ class ViewNode(PGChildNodeView, VacuumSettings):
generate their sql and render
into sql tab
"""
if self.manager.server_type == 'gpdb':
return ''
from pgadmin.browser.server_groups.servers.databases.schemas.utils \
import trigger_definition

View File

@ -0,0 +1,74 @@
{# ============================ Get ACLs ========================= #}
{% if vid %}
SELECT
'datacl' as deftype,
COALESCE(gt.rolname, 'PUBLIC') grantee,
g.rolname grantor,
array_agg(privilege_type) as privileges,
array_agg(is_grantable) as grantable
FROM
(SELECT
d.grantee, d.grantor, d.is_grantable,
CASE d.privilege_type
WHEN 'DELETE' THEN 'd'
WHEN 'INSERT' THEN 'a'
WHEN 'REFERENCES' THEN 'x'
WHEN 'SELECT' THEN 'r'
WHEN 'TRIGGER' THEN 't'
WHEN 'UPDATE' THEN 'w'
WHEN 'TRUNCATE' THEN 'D'
ELSE 'UNKNOWN'
END AS privilege_type
FROM
(SELECT
relacl
FROM
pg_class cl
LEFT OUTER JOIN pg_shdescription descr ON (
cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass)
WHERE
cl.oid = {{ vid }}::OID AND relkind = 'v'
) acl,
(SELECT
(d).grantee AS grantee,
(d).grantor AS grantor,
(d).is_grantable AS is_grantable,
(d).privilege_type AS privilege_type
FROM
(SELECT
u_grantor.oid AS grantor,
grantee.oid AS grantee,
pr.type AS privilege_type,
aclcontains(c.relacl, makeaclitem(grantee.oid, u_grantor.oid, pr.type, true)) AS is_grantable
FROM pg_class c, pg_namespace nc, pg_authid u_grantor, (
SELECT pg_authid.oid, pg_authid.rolname
FROM pg_authid
UNION ALL
SELECT 0::oid AS oid, 'PUBLIC') grantee(oid, rolname),
( SELECT 'SELECT'
UNION ALL
SELECT 'INSERT'
UNION ALL
SELECT 'UPDATE'
UNION ALL
SELECT 'DELETE'
UNION ALL
SELECT 'TRUNCATE'
UNION ALL
SELECT 'REFERENCES'
UNION ALL
SELECT 'TRIGGER') pr(type)
WHERE c.relnamespace = nc.oid
AND (c.relkind = ANY (ARRAY['r'::"char", 'v'::"char"]))
AND aclcontains(c.relacl, makeaclitem(grantee.oid, u_grantor.oid, pr.type, false))
AND (pg_has_role(u_grantor.oid, 'USAGE'::text) OR pg_has_role(grantee.oid, 'USAGE'::text)
OR grantee.rolname = 'PUBLIC'::name)
AND c.oid = {{ vid }}
) d
) d
) d
LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
GROUP BY
g.rolname, gt.rolname
{% endif %}

View File

@ -0,0 +1,6 @@
{
"datacl": {
"type": "VIEW",
"acl": ["a", "r", "w", "d", "D", "x", "t"]
}
}

View File

@ -0,0 +1,20 @@
{#============================Create new view=========================#}
{% if display_comments %}
-- View: {{ conn|qtIdent(data.schema, data.name) }}
-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }};
{% endif %}
{% if data.name and data.schema and data.definition %}
CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }} AS
{{ data.definition.rstrip(';') }};
{% if data.owner %}
ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
OWNER TO {{ conn|qtIdent(data.owner) }};
{% endif %}
{% if data.comment %}
COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }}
IS {{ data.comment|qtLiteral }};
{% endif %}
{% endif %}

View File

@ -0,0 +1,13 @@
{# ====================== Drop/Cascade view by name ===================== #}
{% if vid %}
SELECT
c.relname AS name,
nsp.nspname
FROM
pg_class c
LEFT JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
WHERE
c.relfilenode = {{ vid }};
{% elif (name and nspname) %}
DROP VIEW {{ conn|qtIdent(nspname, name) }} {% if cascade %} CASCADE {% endif %};
{% endif %}

View File

@ -0,0 +1,9 @@
{# ===== fetch new assigned schema id ===== #}
{% if vid %}
SELECT
c.relnamespace as scid
FROM
pg_class c
WHERE
c.oid = {{vid}}::oid;
{% endif %}

View File

@ -0,0 +1,7 @@
{# ===== fetch schema name against schema oid ===== #}
SELECT
nspname
FROM
pg_namespace
WHERE
oid = {{ scid }}::oid;

View File

@ -0,0 +1,6 @@
{# ===== Grant Permissions to User Role on Views/Tables ===== #}
{% import 'macros/schemas/security.macros' as SECLABEL %}
{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
{# ===== We will generate Security Label SQL using macro ===== #}
{% if data.seclabels %}{% for r in data.seclabels %}{{ SECLABEL.SET(conn, 'VIEW', data.name, r.provider, r.label, data.schema) }}{{'\r'}}{% endfor %}{{'\r'}}{% endif %}{% if data.datacl %}
{% for priv in data.datacl %}{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}{% endfor %}{% endif %}

View File

@ -0,0 +1,13 @@
SELECT
c.oid,
c.relname AS name
FROM pg_class c
WHERE
c.relkind = 'v'
{% if (vid and datlastsysoid) %}
AND c.oid = {{vid}}::oid
{% elif scid %}
AND c.relnamespace = {{scid}}::oid
ORDER BY
c.relname
{% endif %}

View File

@ -0,0 +1,57 @@
{% if (vid and datlastsysoid) or scid %}
SELECT
c.oid,
c.xmin,
(CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
c.relname AS name,
nsp.nspname AS schema,
description AS comment,
c.reltablespace AS spcoid,
pg_get_userbyid(c.relowner) AS owner,
pg_get_viewdef(c.oid, true) AS definition,
array_to_string(c.relacl::text[], ', ') AS acl,
{#=============Checks if it is system view================#}
{% if vid and datlastsysoid %}
CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view,
{% endif %}
ARRAY[]::text[] AS seclabels
FROM pg_class c
LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace
LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
WHERE ((c.relhasrules
AND
(EXISTS(
SELECT
r.rulename
FROM
pg_rewrite r
WHERE
((r.ev_class = c.oid) AND (bpchar(r.ev_type) = '1'::bpchar))
))
) AND (c.relkind = 'v'::char))
{% if (vid and datlastsysoid) %}
AND c.oid = {{vid}}::oid
{% elif scid %}
AND c.relnamespace = {{scid}}::oid ORDER BY c.relname
{% endif %}
{% elif type == 'roles' %}
SELECT
pr.rolname
FROM
pg_roles pr
WHERE
pr.rolcanlogin
ORDER BY
pr.rolname
{% elif type == 'schemas' %}
SELECT
nsp.nspname
FROM
pg_namespace nsp
WHERE
(nsp.nspname NOT LIKE E'pg\\_%'
AND nsp.nspname != 'information_schema')
{% endif %}

View File

@ -0,0 +1,67 @@
{# ============================ Update View ========================= #}
{% import 'macros/schemas/security.macros' as SECLABEL %}
{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
{% if data %}
{% set view_name = data.name if data.name else o_data.name %}
{% set view_schema = data.schema if data.schema else o_data.schema %}
{% set def = data.definition.rstrip(';') if data.definition %}
{% if data.name and data.name != o_data.name %}
ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}
{% if data.schema and data.schema != o_data.schema %}
ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }}
SET SCHEMA {{ conn|qtIdent(data.schema) }};
{% endif %}
{% if data.owner and data.owner != o_data.owner %}
ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
OWNER TO {{ conn|qtIdent(data.owner) }};
{% endif %}
{% if def and def != o_data.definition.rstrip(';') %}
CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }}
AS{{ def }};
{% endif %}
{% set old_comment = o_data.comment|default('', true) %}
{% if (data.comment is defined and (data.comment != old_comment)) %}
COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }}
IS {{ data.comment|qtLiteral }};
{% endif %}
{# The SQL generated below will change privileges #}
{% if data.datacl %}
{% if 'deleted' in data.datacl %}
{% for priv in data.datacl.deleted %}
{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
{% endfor %}
{% endif %}
{% if 'changed' in data.datacl %}
{% for priv in data.datacl.changed %}
{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
{% endfor %}
{% endif %}
{% if 'added' in data.datacl %}
{% for priv in data.datacl.added %}
{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
{% endfor %}
{% endif %}
{% endif %}
{# The SQL generated below will change Security Label #}
{% if data.seclabels is not none and data.seclabels|length > 0 %}
{% set seclabels = data.seclabels %}
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
{% for r in seclabels.deleted %}
{{ SECLABEL.UNSET(conn, 'VIEW', data.name, r.provider, data.schema) }}
{% endfor %}
{% endif %}
{% if 'added' in seclabels and seclabels.added|length > 0 %}
{% for r in seclabels.added %}
{{ SECLABEL.SET(conn, 'VIEW', data.name, r.provider, r.label, data.schema) }}
{% endfor %}
{% endif %}
{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
{% for r in seclabels.changed %}
{{ SECLABEL.SET(conn, 'VIEW', data.name, r.provider, r.label, data.schema) }}
{% endfor %}
{% endif %}
{% endif %}
{% endif %}

View File

@ -0,0 +1,4 @@
{# ===== Below will provide view id for last created view ===== #}
{% if data %}
SELECT c.oid, c.relname FROM pg_class c WHERE c.relname = '{{ data.name }}';
{% endif %}

View File

@ -50,6 +50,9 @@ class JobModule(CollectionNodeModule):
conn = manager.connection()
if manager.server_type == 'gpdb':
return False
status, res = conn.execute_scalar("""
SELECT
has_table_privilege('pgagent.pga_job', 'INSERT, SELECT, UPDATE') has_priviledge

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,68 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import json
import uuid
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils
from regression.test_setup import config_data
from pgadmin.browser.utils import is_version_in_range
class VersionInRangeTestCase(BaseTestGenerator):
"""
This class validates the version in range functionality
by defining different version scenarios; where dict of
parameters describes the scenario appended by test name.
"""
scenarios = [
('TestCase for Validating pgversion 8.23 and min_version is 91000, should not show', dict(
sversion=82300,
min_version=90100,
max_version=1000000000,
scenario=2
)),
('TestCase for Validating pgversion 9.2 and should show by default', dict(
sversion=90200,
min_version=0,
max_version=1000000000,
scenario=1
)),
('TestCase for Validating pgversion 9.2 and min/max are None, should show by default', dict(
sversion=90200,
min_version=None,
max_version=None,
scenario=1
)),
('TestCase for Validating pgversion 9.6 and max is lower, should not show', dict(
sversion=90600,
min_version=None,
max_version=90400,
scenario=2
))
]
@classmethod
def setUpClass(cls):
pass
def runTest(self):
"""This function will check version in range functionality."""
if self.scenario == 1:
self.test_result_is_true()
if self.scenario == 2:
self.test_result_is_false()
def test_result_is_true(self):
self.assertTrue(is_version_in_range(self.sversion, self.min_version, self.max_version))
def test_result_is_false(self):
self.assertFalse(is_version_in_range(self.sversion, self.min_version, self.max_version))

View File

@ -19,6 +19,17 @@ from pgadmin.utils.ajax import make_json_response, precondition_required
from config import PG_DEFAULT_DRIVER
def is_version_in_range(sversion, min_ver, max_ver):
assert (max_ver is None or isinstance(max_ver, int))
assert (min_ver is None or isinstance(min_ver, int))
if min_ver is None and max_ver is None:
return True
if min_ver is None or min_ver <= sversion:
if max_ver is None or max_ver >= sversion:
return True
return False
class PGChildModule(object):
"""
@ -40,6 +51,8 @@ class PGChildModule(object):
self.min_ver = 0
self.max_ver = 1000000000
self.server_type = None
self.min_gpdbver = 80323
self.max_gpdbver = 1000000000
super(PGChildModule, self).__init__()
@ -48,20 +61,15 @@ class PGChildModule(object):
if not self.show_node:
return False
sversion = getattr(manager, 'sversion', None)
if (sversion is None or not isinstance(sversion, int)):
return False
if (self.min_ver is None and self.max_ver is None):
return True
assert (self.max_ver is None or isinstance(self.max_ver, int))
assert (self.min_ver is None or isinstance(self.min_ver, int))
assert (self.server_type is None or isinstance(self.server_type, list))
if self.server_type is None or manager.server_type in self.server_type:
if self.min_ver is None or self.min_ver <= sversion:
if self.max_ver is None or self.max_ver >= sversion:
return True
return is_version_in_range(sversion, self.min_gpdbver if manager.server_type == 'gpdb' else self.min_ver,
self.max_gpdbver if manager.server_type == 'gpdb' else self.max_ver)
return False