Resolved few issues (with some improvements) with existing nodes.

This commit takes care of the following issues/improvements.
* Adding missing imports for unauthorised in database module
* Node under Servers Nodes (i.e. Databases, tablespaces, roles nodes)
  need to be inherited from PGChildNodeView (and, not from NodeView) for
  adding server version check for their children.
* Adding statistics for database, and tablespaces in its node (not, yet
  in UI)
* Renaming the camel case methods with proper name.
  (i.e. getSQL -> get_sql, getNewSQL -> get_new_sql, etc.)
* Fixed the functions going beyond the text limit (column: 80) in
  Databases, Roles & Tablespaces modules.
* Fixed the node method of Database module, which was not tested ever.
* We do not need separate SQL template for fetching the name (i.e.
  get_name.sql), using the 'nodes.sql' for the same.
* Optimise the query for fetching ACLs for the database node, we didn't
  require to join certain tables, while fetching only the ACLs.
* Introduced the list of the ACLs (regular and default ACLs for
  different type) supported by each version [Databases Module].
* Renamed the templates 'get_nodes.sql' to' nodes.sql' to make it
  consistent with other modules.
* Removed the checks for the authentication table use, as we don't need
  to expose the password to the users (even the encrypted MD5). Using
  the pg_roles view always for fetching roles/users information now.
* Resolved some typos in unreachable (specially the exceptions
  catchment area.)
* Logging the exception in the application.
* Using qtLiteral, qtIdent properly in the templates (do not assume
  about the types of data.)
* Using tsid as identifier instead of did for the tablespaces.
* Using nodes method of tablespace view for fetching individual node
  information.
* Removing the hardcoded node information from the 'parse_priv_to_db'
  function, and pass on allowed ACLs by the caller nodes.
* Using 'nodes.sql' to fetch name of the template instead of writing
  that in the delete.sql template, which is definitely wrong place to
  fetch the name of the object. [Tablespace Module]
This commit is contained in:
Ashesh Vashi 2016-02-22 11:44:24 +05:30
parent 9274d87c55
commit d5da26876b
50 changed files with 694 additions and 374 deletions

View File

@ -10,16 +10,17 @@ import json
from flask import render_template, make_response, current_app, request, jsonify
from flask.ext.babel import gettext as _
from pgadmin.utils.ajax import make_json_response, \
make_response as ajax_response, internal_server_error
from pgadmin.browser.utils import NodeView
make_response as ajax_response, internal_server_error, unauthorized
from pgadmin.browser.utils import PGChildNodeView
from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
parse_priv_to_db
from pgadmin.browser.collection import CollectionNodeModule
import pgadmin.browser.server_groups.servers as servers
from pgadmin.utils.ajax import precondition_required
from pgadmin.utils.ajax import precondition_required, gone
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from functools import wraps
import re
class DatabaseModule(CollectionNodeModule):
NODE_TYPE = 'database'
@ -72,7 +73,7 @@ class DatabaseModule(CollectionNodeModule):
blueprint = DatabaseModule(__name__)
class DatabaseView(NodeView):
class DatabaseView(PGChildNodeView):
node_type = blueprint.node_type
parent_ids = [
@ -91,7 +92,7 @@ class DatabaseView(NodeView):
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
'sql': [{'get': 'sql'}],
'msql': [{'get': 'msql'}, {'get': 'msql'}],
'stats': [{'get': 'statistics'}],
'stats': [{'get': 'statistics'}, {'get': 'statistics'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'children': [{'get': 'children'}],
@ -99,8 +100,8 @@ class DatabaseView(NodeView):
'connect': [{
'get': 'connect_status', 'post': 'connect', 'delete': 'disconnect'
}],
'get_encodings': [{'get': 'getEncodings'}, {'get': 'getEncodings'}],
'get_ctypes': [{'get': 'getCtypes'}, {'get': 'getCtypes'}],
'get_encodings': [{'get': 'get_encodings'}, {'get': 'get_encodings'}],
'get_ctypes': [{'get': 'get_ctypes'}, {'get': 'get_ctypes'}],
'vopts': [{}, {'get': 'variable_options'}]
})
@ -124,10 +125,8 @@ class DatabaseView(NodeView):
# If DB not connected then return error to browser
if not self.conn.connected():
return precondition_required(
_(
"Connection to the server has been lost!"
_("Connection to the server has been lost!")
)
)
ver = self.manager.version
# we will set template path for sql scripts
@ -143,7 +142,9 @@ class DatabaseView(NodeView):
@check_precondition(action="list")
def list(self, gid, sid):
SQL = render_template("/".join([self.template_path, 'properties.sql']))
SQL = render_template(
"/".join([self.template_path, 'properties.sql'])
)
status, res = self.conn.execute_dict(SQL)
if not status:
@ -157,8 +158,10 @@ class DatabaseView(NodeView):
@check_precondition(action="nodes")
def nodes(self, gid, sid):
res = []
SQL = render_template("/".join([self.template_path, 'get_nodes.sql']))
status, rset = self.conn.execute_2darray(SQL)
SQL = render_template(
"/".join([self.template_path, 'nodes.sql'])
)
status, rset = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=rset)
@ -194,7 +197,10 @@ class DatabaseView(NodeView):
@check_precondition(action="node")
def node(self, gid, sid, did):
SQL = render_template("/".join([self.template_path, 'get_nodes.sql']), did=did)
SQL = render_template(
"/".join([self.template_path, 'nodes.sql']),
did=did, conn=self.conn
)
status, rset = self.conn.execute_2darray(SQL)
if not status:
@ -205,7 +211,7 @@ class DatabaseView(NodeView):
connected=True
else:
conn=self.manager.connection(row['name'])
connected=self.conn.connected()
connected=conn.connected()
return make_json_response(
data=self.blueprint.generate_browser_node(
row['did'],
@ -220,22 +226,35 @@ class DatabaseView(NodeView):
),
status=200
)
return gone(errormsg=_("Couldn't find the database in the server!"))
@check_precondition(action="properties")
def properties(self, gid, sid, did):
SQL = render_template("/".join([self.template_path, 'properties.sql']), did=did)
SQL = render_template(
"/".join([self.template_path, 'properties.sql']),
did=did, conn=self.conn
)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
SQL = render_template("/".join([self.template_path, 'acl.sql']), did=did)
SQL = render_template(
"/".join([self.template_path, 'acl.sql']),
did=did, conn=self.conn
)
status, dataclres = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
res = self.formatdbacl(res, dataclres['rows'])
SQL = render_template("/".join([self.template_path, 'defacl.sql']), did=did)
SQL = render_template(
"/".join([self.template_path, 'defacl.sql']),
did=did, conn=self.conn
)
status, defaclres = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
@ -244,7 +263,10 @@ class DatabaseView(NodeView):
result = res['rows'][0]
# Fetching variable for database
SQL = render_template("/".join([self.template_path, 'get_variables.sql']), did=did)
SQL = render_template(
"/".join([self.template_path, 'get_variables.sql']),
did=did, conn=self.conn
)
status, res1 = self.conn.execute_dict(SQL)
@ -355,15 +377,17 @@ class DatabaseView(NodeView):
}
)
@check_precondition(action="getEncodings")
def getEncodings(self, gid, sid, did=None):
@check_precondition(action="get_encodings")
def get_encodings(self, gid, sid, did=None):
"""
This function to return list of avialable encodings
"""
res = [{ 'label': '', 'value': '' }]
try:
SQL = render_template("/".join([self.template_path, 'get_encodings.sql']))
status, rset = self.conn.execute_2darray(SQL)
SQL = render_template(
"/".join([self.template_path, 'get_encodings.sql'])
)
status, rset = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
@ -380,8 +404,8 @@ class DatabaseView(NodeView):
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition(action="getCtypes")
def getCtypes(self, gid, sid, did=None):
@check_precondition(action="get_ctypes")
def get_ctypes(self, gid, sid, did=None):
"""
This function to return list of available collation/character types
"""
@ -392,16 +416,16 @@ class DatabaseView(NodeView):
{'label': val, 'value': val}
)
try:
SQL = render_template("/".join([self.template_path, 'get_ctypes.sql']))
status, rset = self.conn.execute_2darray(SQL)
SQL = render_template(
"/".join([self.template_path, 'get_ctypes.sql'])
)
status, rset = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
for row in rset['rows']:
if row['cname'] not in default_list:
res.append(
{ 'label': row['cname'], 'value': row['cname'] }
)
res.append({'label': row['cname'], 'value': row['cname']})
return make_json_response(
data=res,
@ -431,7 +455,10 @@ class DatabaseView(NodeView):
)
try:
# The below SQL will execute CREATE DDL only
SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn)
SQL = render_template(
"/".join([self.template_path, 'create.sql']),
data=data, conn=self.conn
)
status, msg = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=msg)
@ -440,7 +467,10 @@ class DatabaseView(NodeView):
data['datacl'] = parse_priv_to_db(data['datacl'], 'DATABASE')
# The below SQL will execute rest DMLs because we can not execute CREATE with any other
SQL = render_template("/".join([self.template_path, 'grant.sql']), data=data, conn=self.conn)
SQL = render_template(
"/".join([self.template_path, 'grant.sql']),
data=data, conn=self.conn
)
SQL = SQL.strip('\n').strip(' ')
if SQL and SQL != "":
status, msg = self.conn.execute_scalar(SQL)
@ -448,7 +478,10 @@ class DatabaseView(NodeView):
return internal_server_error(errormsg=msg)
# We need oid of newly created database
SQL = render_template("/".join([self.template_path, 'properties.sql']), name=data['name'])
SQL = render_template(
"/".join([self.template_path, 'properties.sql']),
name=data['name'], conn=self.conn
)
SQL = SQL.strip('\n').strip(' ')
if SQL and SQL != "":
status, res = self.conn.execute_dict(SQL)
@ -487,20 +520,29 @@ class DatabaseView(NodeView):
if did is not None:
# Fetch the name of database for comparison
SQL = render_template("/".join([self.template_path, 'get_name.sql']), did=did)
status, name = self.conn.execute_scalar(SQL)
status, rset = self.conn.execute_dict(
render_template(
"/".join([self.template_path, 'nodes.sql']),
did=did, conn=self.conn
)
)
if not status:
return internal_server_error(errormsg=name)
return internal_server_error(errormsg=rset)
data['old_name'] = name
if len(rset['rows']) == 0:
return gone(
_("Couldn't find the database on the server!")
)
data['old_name'] = (rset['rows'][0])['name']
if 'name' not in data:
data['name'] = name
data['name'] = data['old_name']
try:
status = self.manager.release(did=did)
conn = self.manager.connection()
for action in ["rename_database", "tablespace"]:
SQL = self.getOfflineSQL(gid, sid, data, did, action)
SQL = self.get_offline_sql(gid, sid, data, did, action)
SQL = SQL.strip('\n').strip(' ')
if SQL and SQL != "":
status, msg = conn.execute_scalar(SQL)
@ -512,7 +554,7 @@ class DatabaseView(NodeView):
self.conn = self.manager.connection(database=data['name'], auto_reconnect=True)
status, errmsg = self.conn.connect()
SQL = self.getOnlineSQL(gid, sid, data, did)
SQL = self.get_online_sql(gid, sid, data, did)
SQL = SQL.strip('\n').strip(' ')
if SQL and SQL != "":
status, msg = self.conn.execute_scalar(SQL)
@ -543,7 +585,10 @@ class DatabaseView(NodeView):
"""Delete the database."""
try:
default_conn = self.manager.connection()
SQL = render_template("/".join([self.template_path, 'delete.sql']), did=did)
SQL = render_template(
"/".join([self.template_path, 'delete.sql']),
did=did, conn=self.conn
)
status, res = default_conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
@ -559,7 +604,10 @@ class DatabaseView(NodeView):
status = self.manager.release(did=did)
SQL = render_template("/".join([self.template_path, 'delete.sql']), datname=res, conn=self.conn)
SQL = render_template(
"/".join([self.template_path, 'delete.sql']),
datname=res, conn=self.conn
)
status, msg = default_conn.execute_scalar(SQL)
if not status:
@ -587,40 +635,57 @@ class DatabaseView(NodeView):
except ValueError:
data[k] = v
try:
SQL = self.getSQL(gid, sid, data, did)
SQL = SQL.strip('\n').strip(' ')
status, res = self.get_sql(gid, sid, data, did)
if not status:
return res
SQL = res.strip('\n').strip(' ')
return make_json_response(
data=SQL,
status=200
)
except Exception as e:
current_app.logger.exception(e)
return make_json_response(
data="-- modified SQL",
status=200
)
def getSQL(self, gid, sid, data, did=None):
def get_sql(self, gid, sid, data, did=None):
SQL = ''
if did is not None:
# Fetch the name of database for comparison
SQL = render_template("/".join([self.template_path, 'get_name.sql']), did=did)
status, name = self.conn.execute_scalar(SQL)
status, rset = self.conn.execute_dict(
render_template(
"/".join([self.template_path, 'nodes.sql']),
did=did, conn=self.conn
)
)
if not status:
return internal_server_error(errormsg=name)
return False, internal_server_error(errormsg=rset)
data['old_name'] = name
if len(rset['rows']) == 0:
return False, gone(
_("Couldn't find the database on the server!")
)
data['old_name'] = (rset['rows'][0])['name']
if 'name' not in data:
data['name'] = name
data['name'] = data['old_name']
SQL = ''
for action in ["rename_database", "tablespace"]:
SQL += self.getOfflineSQL(gid, sid, data, did, action)
SQL += self.get_offline_sql(gid, sid, data, did, action)
SQL += self.getOnlineSQL(gid, sid, data, did)
SQL += self.get_online_sql(gid, sid, data, did)
else:
SQL += self.getNewSQL(gid, sid, data, did)
return SQL
SQL += self.get_new_sql(gid, sid, data, did)
def getNewSQL(self, gid, sid, data, did=None):
return True, SQL
def get_new_sql(self, gid, sid, data, did=None):
"""
Generates sql for creating new database.
"""
@ -630,52 +695,84 @@ class DatabaseView(NodeView):
for arg in required_args:
if arg not in data:
return " -- definition incomplete"
return _(" -- definition incomplete")
acls = []
try:
acls = render_template(
"/".join([self.template_path, 'allowed_privs.json'])
)
acls = json.loads(acls)
except Exception as e:
current_app.logger.exception(e)
# Privileges
if 'datacl' in data:
data['datacl'] = parse_priv_to_db(data['datacl'], 'DATABASE')
for aclcol in acls:
if aclcol in data:
allowedacl = acls[aclcol]
data[aclcol] = parse_priv_to_db(
data[aclcol], allowedacl['acl']
)
# Default privileges
for key in ['deftblacl', 'defseqacl', 'deffuncacl', 'deftypeacl']:
if key in data and data[key] is not None:
data[key] = parse_priv_to_db(data[key])
SQL = render_template("/".join([self.template_path, 'create.sql']), data=data)
SQL = render_template(
"/".join([self.template_path, 'create.sql']),
data=data, conn=self.conn
)
SQL += "\n"
SQL += render_template("/".join([self.template_path, 'grant.sql']), data=data)
SQL += render_template(
"/".join([self.template_path, 'grant.sql']),
data=data, conn=self.conn
)
return SQL
def getOnlineSQL(self, gid, sid, data, did=None):
def get_online_sql(self, gid, sid, data, did=None):
"""
Generates sql for altering database which don not require
database to be disconnected before applying.
"""
acls = []
try:
acls = render_template(
"/".join([self.template_path, 'allowed_privs.json'])
)
acls = json.loads(acls)
except Exception as e:
current_app.logger.exception(e)
for key in ['datacl', 'deftblacl', 'defseqacl', 'deffuncacl', 'deftypeacl']:
if key in data and data[key] is not None:
if 'added' in data[key]:
data[key]['added'] = parse_priv_to_db(data[key]['added'])
if 'changed' in data[key]:
data[key]['changed'] = parse_priv_to_db(data[key]['changed'])
if 'deleted' in data[key]:
data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'])
# Privileges
for aclcol in acls:
if aclcol in data:
allowedacl = acls[aclcol]
return render_template("/".join([self.template_path, 'alter_online.sql']), data=data, conn=self.conn)
for key in ['added', 'changed', 'deleted']:
if key in data[aclcol]:
data[aclcol][key] = parse_priv_to_db(
data[aclcol][key], allowedacl['acl']
)
def getOfflineSQL(self, gid, sid, data, did=None, action=None):
return render_template(
"/".join([self.template_path, 'alter_online.sql']),
data=data, conn=self.conn
)
def get_offline_sql(self, gid, sid, data, did=None, action=None):
"""
Generates sql for altering database which require
database to be disconnected before applying.
"""
return render_template("/".join([self.template_path, 'alter_offline.sql']),
data=data, conn=self.conn, action=action)
return render_template(
"/".join([self.template_path, 'alter_offline.sql']),
data=data, conn=self.conn, action=action
)
@check_precondition(action="variable_options")
def variable_options(self, gid, sid):
res = []
SQL = render_template("/".join([self.template_path, 'variables.sql']))
SQL = render_template(
"/".join([self.template_path, 'variables.sql'])
)
status, rset = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=rset)
@ -684,23 +781,55 @@ class DatabaseView(NodeView):
status=200
)
@check_precondition()
def statistics(self, gid, sid, did=None):
"""
statistics
Returns the statistics for a particular database if did is specified,
otherwise it will return statistics for all the databases in that
server.
"""
status, res = self.conn.execute_dict(
render_template(
"/".join([self.template_path, 'stats.sql']),
did=did, conn=self.conn
)
)
if not status:
return internal_server_error(errormsg=res)
return make_json_response(
data=res['rows'],
status=200
)
@check_precondition(action="sql")
def sql(self, gid, sid, did):
"""
This function will generate sql for sql panel
"""
SQL = render_template("/".join([self.template_path, 'properties.sql']), did=did)
SQL = render_template(
"/".join([self.template_path, 'properties.sql']),
did=did, conn=self.conn
)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
SQL = render_template("/".join([self.template_path, 'acl.sql']), did=did)
SQL = render_template(
"/".join([self.template_path, 'acl.sql']),
did=did, conn=self.conn
)
status, dataclres = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
res = self.formatdbacl(res, dataclres['rows'])
SQL = render_template("/".join([self.template_path, 'defacl.sql']), did=did)
SQL = render_template(
"/".join([self.template_path, 'defacl.sql']),
did=did, conn=self.conn
)
status, defaclres = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
@ -709,7 +838,10 @@ class DatabaseView(NodeView):
result = res['rows'][0]
SQL = render_template("/".join([self.template_path, 'get_variables.sql']), did=did)
SQL = render_template(
"/".join([self.template_path, 'get_variables.sql']),
did=did, conn=self.conn
)
status, res1 = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res1)
@ -717,7 +849,7 @@ class DatabaseView(NodeView):
frmtd_reslt = self.formatter(result, res1)
result.update(frmtd_reslt)
SQL = self.getNewSQL(gid, sid, result, did)
SQL = self.get_new_sql(gid, sid, result, did)
return ajax_response(response=SQL)

View File

@ -1,4 +1,7 @@
SELECT 'relacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
SELECT
'datacl' AS deftype, COALESCE(gt.rolname, 'public') AS grantee,
g.rolname AS grantor, array_agg(privilege_type) AS privileges,
array_agg(is_grantable) AS grantable
FROM
(SELECT
d.grantee, d.grantor, d.is_grantable,
@ -18,16 +21,15 @@ FROM
ELSE 'UNKNOWN'
END AS privilege_type
FROM
(SELECT datacl FROM pg_database db
LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace=ta.OID
LEFT OUTER JOIN pg_shdescription descr ON (
db.oid=descr.objoid AND descr.classoid='pg_database'::regclass)
WHERE db.oid = {{ did|qtLiteral }}::OID
) acl,
(SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable AS is_grantable, (d).privilege_type AS privilege_type
FROM (SELECT aclexplode(datacl) as d FROM pg_database db1
WHERE db1.oid = {{ did|qtLiteral }}::OID) a) d
(SELECT
(d).grantee AS grantee, (d).grantor AS grantor,
(d).is_grantable AS is_grantable,
(d).privilege_type AS privilege_type
FROM
(SELECT aclexplode(db.datacl) AS d FROM pg_database db
WHERE db.oid = {{ did|qtLiteral }}::OID) a
) 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
GROUP BY g.rolname, gt.rolname;

View File

@ -0,0 +1,26 @@
{# List down allowed privileges for PostgreSQL/PPAS 9.1, or less #}
{#
Format for allowed privileges are:
"acl_col": {
"type": "name",
"acl": [...]
}
#}
{
"datacl": {
"type": "DATABASE",
"acl": ["c", "C", "T"]
},
"deftblacl": {
"type": "TABLE",
"acl": ["r", "a", "w", "d", "D", "x", "t"]
},
"defseqacl": {
"type": "SEQUENCE",
"acl": ["U", "r", "a"]
},
"deffuncacl": {
"type": "FUNCTION",
"acl": ["X"]
}
}

View File

@ -1,4 +0,0 @@
{# We need database name #}
{% if did %}
SELECT db.datname as name FROM pg_database as db WHERE db.oid = {{did}}
{% endif %}

View File

@ -1,7 +1,8 @@
SELECT
db.oid as did, db.datname as name, ta.spcname as spcname, db.datallowconn,
has_database_privilege(db.oid, 'CREATE') as cancreate
has_database_privilege(db.oid, 'CREATE') as cancreate, datdba as owner
FROM
pg_database db
LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace = ta.oid
{% if did %}WHERE db.oid={{did}}::int {% endif %}
LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace = ta.oid{% if did %}
WHERE db.oid={{ did|qtLiteral }}::OID{% endif %};

View File

@ -0,0 +1,13 @@
SELECT
db.datid as oid, db.datname, numbackends, xact_commit, xact_rollback, blks_read,
blks_hit, stats_reset, slave.confl_tablespace, slave.confl_lock,
slave.confl_snapshot, slave.confl_bufferpin, slave.confl_deadlock{% if has_size %},
pg_size_pretty(pg_database_size(db.datid)) as size
FROM
pg_stat_database db
LEFT JOIN pg_stat_database_conflicts slave ON db.datid=slave.datid
{% if did %}
WHERE
did = {{ conn|qtIdent(did) }}::OID
{% endif %}
ORDER BY db.datname;

View File

@ -1,4 +1,7 @@
SELECT 'datacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
SELECT
'datacl' AS deftype, COALESCE(gt.rolname, 'public') AS 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,
@ -18,16 +21,15 @@ FROM
ELSE 'UNKNOWN'
END AS privilege_type
FROM
(SELECT datacl FROM pg_database db
LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace=ta.OID
LEFT OUTER JOIN pg_shdescription descr ON (
db.oid=descr.objoid AND descr.classoid='pg_database'::regclass)
WHERE db.oid = {{ did|qtLiteral }}::OID
) acl,
(SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable AS is_grantable, (d).privilege_type AS privilege_type
FROM (SELECT aclexplode(datacl) as d FROM pg_database db1
WHERE db1.oid = {{ did|qtLiteral }}::OID) a) d
(SELECT
(d).grantee AS grantee, (d).grantor AS grantor,
(d).is_grantable AS is_grantable,
(d).privilege_type AS privilege_type
FROM
(SELECT aclexplode(db.datacl) AS d FROM pg_database db
WHERE db.oid = {{ did|qtLiteral }}::OID) a
) 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
GROUP BY g.rolname, gt.rolname;

View File

@ -0,0 +1,30 @@
{# List down allowed privileges for PostgreSQL/PPAS 9.1, or less #}
{#
Format for allowed privileges are:
"acl_col": {
"type": "name",
"acl": [...]
}
#}
{
"datacl": {
"type": "DATABASE",
"acl": ["c", "C", "T"]
},
"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

@ -1,4 +0,0 @@
{# We need database name #}
{% if did %}
SELECT db.datname as name FROM pg_database as db WHERE db.oid = {{did}}
{% endif %}

View File

@ -1,7 +1,8 @@
SELECT
db.oid as did, db.datname as name, ta.spcname as spcname, db.datallowconn,
has_database_privilege(db.oid, 'CREATE') as cancreate
has_database_privilege(db.oid, 'CREATE') as cancreate, , datdba as owner
FROM
pg_database db
LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace = ta.oid
{% if did %}WHERE db.oid={{did}}::int {% endif %}
LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace = ta.oid{% if did %}
WHERE db.oid={{ did|qtLiteral }}::OID {% endif %};

View File

@ -0,0 +1,13 @@
SELECT
db.datid as oid, db.datname, numbackends, xact_commit, xact_rollback, blks_read,
blks_hit, stats_reset, slave.confl_tablespace, slave.confl_lock,
slave.confl_snapshot, slave.confl_bufferpin, slave.confl_deadlock{% if has_size %},
pg_size_pretty(pg_database_size(db.datid)) as size
FROM
pg_stat_database db
LEFT JOIN pg_stat_database_conflicts slave ON db.datid=slave.datid
{% if did %}
WHERE
did = {{ conn|qtIdent(did) }}::OID
{% endif %}
ORDER BY db.datname;

View File

@ -1,4 +1,7 @@
SELECT 'datacl' AS deftype, COALESCE(gt.rolname, 'public') AS grantee, g.rolname AS grantor, array_agg(privilege_type) AS privileges, array_agg(is_grantable) AS grantable
SELECT
'datacl' AS deftype, COALESCE(gt.rolname, 'public') AS grantee,
g.rolname AS grantor, array_agg(privilege_type) AS privileges,
array_agg(is_grantable) AS grantable
FROM
(SELECT
d.grantee, d.grantor, d.is_grantable,
@ -18,16 +21,15 @@ FROM
ELSE 'UNKNOWN'
END AS privilege_type
FROM
(SELECT datacl FROM pg_database db
LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace=ta.OID
LEFT OUTER JOIN pg_shdescription descr ON (
db.oid=descr.objoid AND descr.classoid='pg_database'::regclass)
{% if did %}
WHERE db.oid = {{ did|qtLiteral }}::OID
{% endif %}
) acl,
aclexplode(datacl) d
(SELECT
(d).grantee AS grantee, (d).grantor AS grantor,
(d).is_grantable AS is_grantable,
(d).privilege_type AS privilege_type
FROM
(SELECT aclexplode(db.datacl) AS d FROM pg_database db
WHERE db.oid = {{ did|qtLiteral }}::OID) a
) 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
GROUP BY g.rolname, gt.rolname;

View File

@ -0,0 +1,30 @@
{# List down allowed privileges for PostgreSQL/PPAS 9.1, or less #}
{#
Format for allowed privileges are:
"acl_col": {
"type": "name",
"acl": [...]
}
#}
{
"datacl": {
"type": "DATABASE",
"acl": ["c", "C", "T"]
},
"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

@ -1,4 +0,0 @@
{# We need database name #}
{% if did %}
SELECT db.datname as name FROM pg_database as db WHERE db.oid = {{did}}
{% endif %}

View File

@ -1,7 +1,8 @@
SELECT
db.oid as did, db.datname as name, ta.spcname as spcname, db.datallowconn,
has_database_privilege(db.oid, 'CREATE') as cancreate
has_database_privilege(db.oid, 'CREATE') as cancreate, datdba as owner
FROM
pg_database db
LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace = ta.oid
{% if did %}WHERE db.oid={{did}}::int {% endif %}
LEFT OUTER JOIN pg_tablespace ta ON db.dattablespace = ta.oid{% if did %}
WHERE db.oid={{ did|qtLiteral }}::OID{% endif %};

View File

@ -0,0 +1,13 @@
SELECT
db.datid as oid, db.datname, numbackends, xact_commit, xact_rollback, blks_read,
blks_hit, stats_reset, slave.confl_tablespace, slave.confl_lock,
slave.confl_snapshot, slave.confl_bufferpin, slave.confl_deadlock{% if has_size %},
pg_size_pretty(pg_database_size(db.datid)) as size
FROM
pg_stat_database db
LEFT JOIN pg_stat_database_conflicts slave ON db.datid=slave.datid
{% if did %}
WHERE
did = {{ conn|qtIdent(did) }}::OID
{% endif %}
ORDER BY db.datname;

View File

@ -6,13 +6,13 @@
# This software is released under the PostgreSQL Licence
#
##########################################################################
from flask import render_template, request, current_app, jsonify
from flask import render_template, request, jsonify
from flask.ext.babel import gettext as _
from pgadmin.utils.ajax import make_json_response, \
make_response as ajax_response, precondition_required, \
internal_server_error, forbidden, \
not_implemented, success_return
from pgadmin.browser.utils import NodeView
not_implemented, success_return, gone
from pgadmin.browser.utils import PGChildNodeView
from pgadmin.browser.collection import CollectionNodeModule
import pgadmin.browser.server_groups as sg
from pgadmin.utils.driver import get_driver
@ -76,7 +76,7 @@ class RoleModule(CollectionNodeModule):
blueprint = RoleModule(__name__)
class RoleView(NodeView):
class RoleView(PGChildNodeView):
node_type = 'role'
parent_ids = [
@ -453,14 +453,11 @@ rolmembership:{
u'rolvaliduntil', u'rolpassword'
]
auth_tbl=False
check_permission=False
fetch_name=False
forbidden_msg = None
if action in ['list', 'properties']:
auth_tbl = True
elif action in ['drop', 'update']:
if action in ['drop', 'update']:
check_permission = True
fetch_name = True
if action == 'drop':
@ -480,28 +477,13 @@ rolmembership:{
'rid' in kwargs and kwargs['rid'] != -1):
fetch_name = True
if auth_tbl:
status, res = self.conn.execute_scalar(
"SELECT has_table_privilege('pg_authid', 'SELECT')"
)
if not status:
return internal_server_error(
_(
"Error checking the permission to the pg_authid!\n{0}"
).format(res)
)
self.role_tbl = 'pg_authid' if res else 'pg_roles'
else:
self.role_tbl = 'pg_roles'
if check_permission:
user = self.manager.user_info
if not user['is_superuser'] and not user['can_create_role']:
if (action != 'update' or
'rid' in kwargs and kwargs['rid'] != -1 and
user['id'] != rid):
user['id'] != kwargs['rid']):
return forbidden(forbidden_msg)
if fetch_name:
@ -540,8 +522,8 @@ rolmembership:{
@check_precondition(action='list')
def list(self, gid, sid):
status, res = self.conn.execute_dict(
render_template(self.sql_path + 'properties.sql',
role_tbl=self.role_tbl
render_template(
self.sql_path + 'properties.sql'
)
)
@ -563,16 +545,14 @@ rolmembership:{
def nodes(self, gid, sid):
status, rset = self.conn.execute_2darray(
render_template(self.sql_path + 'nodes.sql',
role_tbl=self.role_tbl
)
)
render_template(self.sql_path + 'nodes.sql')
)
if not status:
return internal_server_error(
_(
"Error fetching the roles information from the database server!\n{0}"
).format(res)
).format(rset)
)
res = []
@ -596,17 +576,17 @@ rolmembership:{
def node(self, gid, sid, rid):
status, rset = self.conn.execute_2darray(
render_template(self.sql_path + 'nodes.sql',
rid=rid,
role_tbl=self.role_tbl
)
render_template(
self.sql_path + 'nodes.sql',
rid=rid
)
)
if not status:
return internal_server_error(
_(
"Error fetching the roles information from the database server!\n{0}"
).format(res)
).format(rset)
)
for row in rset['rows']:
@ -653,8 +633,8 @@ rolmembership:{
def properties(self, gid, sid, rid):
status, res = self.conn.execute_dict(
render_template(self.sql_path + 'properties.sql',
role_tbl=self.role_tbl,
render_template(
self.sql_path + 'properties.sql',
rid=rid
)
)
@ -692,8 +672,7 @@ rolmembership:{
def sql(self, gid, sid, rid):
status, res = self.conn.execute_scalar(
render_template(
self.sql_path + 'sql.sql',
role_tbl=self.role_tbl
self.sql_path + 'sql.sql'
),
dict({'rid':rid})
)
@ -731,9 +710,7 @@ rolmembership:{
)
status, rid = self.conn.execute_scalar(
"SELECT oid FROM {0} WHERE rolname = %(rolname)s".format(
self.role_tbl
),
"SELECT oid FROM pg_roles WHERE rolname = %(rolname)s",
{'rolname': self.request[u'rolname']}
)
@ -745,8 +722,7 @@ rolmembership:{
status, rset = self.conn.execute_dict(
render_template(self.sql_path + 'nodes.sql',
rid=rid,
role_tbl=self.role_tbl
rid=rid
)
)
@ -754,7 +730,7 @@ rolmembership:{
return internal_server_error(
_(
"Error fetching the roles information from the database server!\n{0}"
).format(res)
).format(rset)
)
for row in rset['rows']:
return jsonify(
@ -793,8 +769,7 @@ rolmembership:{
status, rset = self.conn.execute_dict(
render_template(self.sql_path + 'nodes.sql',
rid=rid,
role_tbl=self.role_tbl
rid=rid
)
)
@ -802,7 +777,7 @@ rolmembership:{
return internal_server_error(
_(
"Error fetching the roles information from the database server!\n{0}"
).format(res)
).format(rset)
)
for row in rset['rows']:

View File

@ -1,8 +1,8 @@
SELECT
r.oid, r.rolname, r.rolcanlogin, r.rolsuper
FROM
{{ role_tbl }} r
pg_roles r
{% if rid %}
WHERE r.oid = {{ rid }}::int
WHERE r.oid = {{ rid|qtLiteral }}::OID
{% endif %}
ORDER BY r.rolcanlogin, r.rolname

View File

@ -9,8 +9,8 @@ SELECT
LEFT JOIN pg_catalog.pg_roles rm ON (rm.oid = am.roleid)
) rolmembership
FROM
{{ role_tbl }} r
pg_roles r
{% if rid %}
WHERE r.oid = {{ rid }}::int
WHERE r.oid = {{ rid|qtIdent }}::OID
{% endif %}
ORDER BY r.rolcanlogin, r.rolname

View File

@ -19,7 +19,7 @@ FROM
-- PostgreSQL < 9.5
CASE WHEN rolsuper AND NOT rolcatupdate THEN E'\n\nUPDATE pg_authid SET rolcatupdate=false WHERE rolname=' || pg_catalog.quote_literal(rolname) || ';' ELSE '' END AS sql
FROM
{{ role_tbl }} r
pg_roles r
WHERE
r.oid=%(rid)s::OID
UNION ALL

View File

@ -1,8 +1,8 @@
SELECT
r.oid, r.rolname, r.rolcanlogin, r.rolsuper
FROM
{{ role_tbl }} r
pg_roles r
{% if rid %}
WHERE r.oid = {{ rid }}::int
WHERE r.oid = {{ rid|qtLiteral }}::OID
{% endif %}
ORDER BY r.rolcanlogin, r.rolname

View File

@ -9,8 +9,8 @@ SELECT
LEFT JOIN pg_catalog.pg_roles rm ON (rm.oid = am.roleid)
) rolmembership
FROM
{{ role_tbl }} r
pg_roles r
{% if rid %}
WHERE r.oid = {{ rid }}::int
WHERE r.oid = {{ rid|qtLiteral }}::int
{% endif %}
ORDER BY r.rolcanlogin, r.rolname

View File

@ -19,7 +19,7 @@ FROM
-- PostgreSQL < 9.5
CASE WHEN rolsuper AND NOT rolcatupdate THEN E'\n\nUPDATE pg_authid SET rolcatupdate=false WHERE rolname=' || pg_catalog.quote_literal(rolname) || ';' ELSE '' END AS sql
FROM
{{ role_tbl }} r
pg_roles r
WHERE
r.oid=%(rid)s::OID
UNION ALL

View File

@ -1,8 +1,8 @@
SELECT
r.oid, r.rolname, r.rolcanlogin, r.rolsuper
FROM
{{ role_tbl }} r
pg_roles r
{% if rid %}
WHERE r.oid = {{ rid }}::int
WHERE r.oid = {{ rid|qtLiteral }}::int
{% endif %}
ORDER BY r.rolcanlogin, r.rolname

View File

@ -10,8 +10,8 @@ SELECT
) rolmembership,
(SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=r.oid) AS seclabels
FROM
{{ role_tbl }} r
pg_roles r
{% if rid %}
WHERE r.oid = {{ rid }}::int
WHERE r.oid = {{ rid|qtLiteral }}::int
{% endif %}
ORDER BY r.rolcanlogin, r.rolname

View File

@ -21,7 +21,7 @@ FROM
-- PostgreSQL < 9.5
CASE WHEN rolsuper AND NOT rolcatupdate THEN E'\n\nUPDATE pg_authid SET rolcatupdate=false WHERE rolname=' || pg_catalog.quote_literal(rolname) || ';' ELSE '' END AS sql
FROM
{{ role_tbl }} r
pg_roles r
WHERE
r.oid=%(rid)s::OID
UNION ALL

View File

@ -1,8 +1,8 @@
SELECT
r.oid, r.rolname, r.rolcanlogin, r.rolsuper
FROM
{{ role_tbl }} r
pg_roles r
{% if rid %}
WHERE r.oid = {{ rid }}::int
WHERE r.oid = {{ rid|qtLiteral }}::int
{% endif %}
ORDER BY r.rolcanlogin, r.rolname

View File

@ -10,8 +10,8 @@ SELECT
) AS rolmembership,
(SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=r.oid) AS seclabels
FROM
{{ role_tbl }} r
pg_roles r
{% if rid %}
WHERE r.oid = {{ rid }}::int
WHERE r.oid = {{ rid|qtLiteral }}::int
{% endif %}
ORDER BY r.rolcanlogin, r.rolname

View File

@ -19,7 +19,7 @@ FROM
CASE WHEN rolconnlimit > 0 THEN E'\n CONNECTION LIMIT ' || rolconnlimit ELSE '' END ||
CASE WHEN rolvaliduntil IS NOT NULL THEN E'\n VALID UNTIL ' || quote_literal(rolvaliduntil::text) ELSE ';' END AS sql
FROM
{{ role_tbl }} r
pg_roles r
WHERE
r.oid=%(rid)s::OID
UNION ALL

View File

@ -7,11 +7,11 @@
#
##########################################################################
import json
from flask import render_template, make_response, request, jsonify
from flask import render_template, make_response, request, jsonify, current_app
from flask.ext.babel import gettext
from pgadmin.utils.ajax import make_json_response, \
make_response as ajax_response, internal_server_error
from pgadmin.browser.utils import NodeView
make_response as ajax_response, internal_server_error, gone
from pgadmin.browser.utils import PGChildNodeView
from pgadmin.browser.collection import CollectionNodeModule
import pgadmin.browser.server_groups.servers as servers
from pgadmin.utils.ajax import precondition_required
@ -54,7 +54,7 @@ class TablespaceModule(CollectionNodeModule):
blueprint = TablespaceModule(__name__)
class TablespaceView(NodeView):
class TablespaceView(PGChildNodeView):
node_type = blueprint.node_type
parent_ids = [
@ -62,7 +62,7 @@ class TablespaceView(NodeView):
{'type': 'int', 'id': 'sid'}
]
ids = [
{'type': 'int', 'id': 'did'}
{'type': 'int', 'id': 'tsid'}
]
operations = dict({
@ -70,11 +70,11 @@ class TablespaceView(NodeView):
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
{'get': 'list', 'post': 'create'}
],
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
'nodes': [{'get': 'nodes'}, {'get': 'nodes'}],
'children': [{'get': 'children'}],
'sql': [{'get': 'sql'}],
'msql': [{'get': 'msql'}, {'get': 'msql'}],
'stats': [{'get': 'statistics'}],
'stats': [{'get': 'statistics'}, {'get': 'statistics'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'module.js': [{}, {}, {'get': 'module_js'}],
@ -102,17 +102,20 @@ class TablespaceView(NodeView):
"""
@wraps(f)
def wrap(*args, **kwargs):
# Here args[0] will hold self & kwargs will hold gid,sid,did
# Here args[0] will hold self & kwargs will hold gid,sid,tsid
self = args[0]
self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
self.conn = self.manager.connection()
# If DB not connected then return error to browser
if not self.conn.connected():
current_app.logger.warning(
"Connection to the server has been lost!"
)
return precondition_required(
gettext(
"Connection to the server has been lost!"
)
"Connection to the server has been lost!"
)
)
ver = self.manager.version
@ -122,13 +125,19 @@ class TablespaceView(NodeView):
self.template_path = 'tablespaces/sql/9.1_plus'
else:
self.template_path = 'tablespaces/sql/pre_9.1'
current_app.logger.debug(
"Using the template path: %s", self.template_path
)
return f(*args, **kwargs)
return wrap
@check_precondition
def list(self, gid, sid):
SQL = render_template("/".join([self.template_path, 'properties.sql']))
SQL = render_template(
"/".join([self.template_path, 'properties.sql']),
conn=self.conn
)
status, res = self.conn.execute_dict(SQL)
if not status:
@ -139,9 +148,12 @@ class TablespaceView(NodeView):
)
@check_precondition
def nodes(self, gid, sid):
def nodes(self, gid, sid, tsid=None):
res = []
SQL = render_template("/".join([self.template_path, 'properties.sql']))
SQL = render_template(
"/".join([self.template_path, 'nodes.sql']),
tsid=tsid, conn=self.conn
)
status, rset = self.conn.execute_2darray(SQL)
if not status:
return internal_server_error(errormsg=rset)
@ -160,11 +172,11 @@ class TablespaceView(NodeView):
status=200
)
def _formatter(self, data, did=None):
def _formatter(self, data, tsid=None):
"""
Args:
data: dict of query result
did: tablespace oid
tsid: tablespace oid
Returns:
It will return formatted output of collections
@ -188,8 +200,10 @@ class TablespaceView(NodeView):
data['seclabels'] = seclabels
# We need to parse & convert ACL coming from database to json format
SQL = render_template("/".join([self.template_path, 'acl.sql']),
did=did)
SQL = render_template(
"/".join([self.template_path, 'acl.sql']),
tsid=tsid, conn=self.conn
)
status, acl = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=acl)
@ -208,16 +222,18 @@ class TablespaceView(NodeView):
return data
@check_precondition
def properties(self, gid, sid, did):
SQL = render_template("/".join([self.template_path, 'properties.sql']),
did=did, conn=self.conn)
def properties(self, gid, sid, tsid):
SQL = render_template(
"/".join([self.template_path, 'properties.sql']),
tsid=tsid, conn=self.conn
)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
# Making copy of output for future use
copy_data = dict(res['rows'][0])
copy_data = self._formatter(copy_data, did)
copy_data = self._formatter(copy_data, tsid)
return ajax_response(
response=copy_data,
@ -250,49 +266,61 @@ class TablespaceView(NodeView):
# To format privileges coming from client
if 'spcacl' in data:
data['spcacl'] = parse_priv_to_db(data['spcacl'], 'TABLESPACE')
data['spcacl'] = parse_priv_to_db(data['spcacl'], ['C'])
try:
SQL = render_template("/".join([self.template_path, 'create.sql']),
data=data, conn=self.conn)
SQL = render_template(
"/".join([self.template_path, 'create.sql']),
data=data, conn=self.conn
)
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
SQL = render_template("/".join([self.template_path, 'alter.sql']),
data=data, conn=self.conn)
SQL = render_template(
"/".join([self.template_path, 'alter.sql']),
data=data, conn=self.conn
)
# Checking if we are not executing empty query
if SQL and SQL.strip('\n') and SQL.strip(' '):
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
# To fetch the oid of newly created tablespace
SQL = render_template("/".join([self.template_path, 'alter.sql']),
tablespace=data['name'], conn=self.conn)
status, did = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=did)
# To fetch the oid of newly created tablespace
SQL = render_template(
"/".join([self.template_path, 'alter.sql']),
tablespace=data['name'], conn=self.conn
)
status, tsid = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=tsid)
return jsonify(
node=self.blueprint.generate_browser_node(
did,
tsid,
sid,
data['name'],
icon="icon-tablespace"
)
)
except Exception as e:
current_app.logger.exception(e)
return internal_server_error(errormsg=str(e))
@check_precondition
def update(self, gid, sid, did):
def update(self, gid, sid, tsid):
"""
This function will update tablespace object
"""
data = request.form if request.form else json.loads(request.data.decode())
try:
SQL = self.getSQL(gid, sid, data, did)
SQL = self.get_sql(gid, sid, data, tsid)
if SQL and SQL.strip('\n') and SQL.strip(' '):
status, res = self.conn.execute_scalar(SQL)
if not status:
@ -302,7 +330,7 @@ class TablespaceView(NodeView):
success=1,
info="Tablespace updated",
data={
'id': did,
'id': tsid,
'sid': sid,
'gid': gid
}
@ -312,30 +340,44 @@ class TablespaceView(NodeView):
success=1,
info="Nothing to update",
data={
'id': did,
'id': tsid,
'sid': sid,
'gid': gid
}
)
except Exception as e:
current_app.logger.exception(e)
return internal_server_error(errormsg=str(e))
@check_precondition
def delete(self, gid, sid, did):
def delete(self, gid, sid, tsid):
"""
This function will drop the tablespace object
"""
try:
# Get name for tablespace from did
SQL = render_template("/".join([self.template_path, 'delete.sql']),
did=did, conn=self.conn)
status, tsname = self.conn.execute_scalar(SQL)
# Get name for tablespace from tsid
status, rset = self.conn.execute_dict(
render_template(
"/".join([self.template_path, 'nodes.sql']),
tsid=tsid, conn=self.conn
)
)
if not status:
return internal_server_error(errormsg=tsname)
return internal_server_error(errormsg=rset)
if len(rset['rows']) != 1:
return gone(
errormsg=gettext("Couldn't the tablespace in the server!")
)
# drop tablespace
SQL = render_template("/".join([self.template_path, 'delete.sql']),
tsname=tsname, conn=self.conn)
SQL = render_template(
"/".join([self.template_path, 'delete.sql']),
tsname=(rset['rows'][0])['name'], conn=self.conn
)
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
@ -344,17 +386,18 @@ class TablespaceView(NodeView):
success=1,
info=gettext("Tablespace dropped"),
data={
'id': did,
'id': tsid,
'sid': sid,
'gid': gid,
}
)
}
)
except Exception as e:
current_app.logger.exception(e)
return internal_server_error(errormsg=str(e))
@check_precondition
def msql(self, gid, sid, did=None):
def msql(self, gid, sid, tsid=None):
"""
This function to return modified SQL
"""
@ -362,20 +405,22 @@ class TablespaceView(NodeView):
for k, v in request.args.items():
try:
data[k] = json.loads(v)
except ValueError:
except ValueError as ve:
current_app.logger.exception(ve)
data[k] = v
try:
SQL = self.getSQL(gid, sid, data, did)
if SQL and SQL.strip('\n') and SQL.strip(' '):
return make_json_response(
data=SQL,
status=200
)
SQL = self.get_sql(gid, sid, data, tsid)
except Exception as e:
current_app.logger.exception(e)
return internal_server_error(errormsg=str(e))
def getSQL(self, gid, sid, data, did=None):
if SQL and SQL.strip('\n') and SQL.strip(' '):
return make_json_response(
data=SQL,
status=200
)
def get_sql(self, gid, sid, data, tsid=None):
"""
This function will genrate sql from model/properties data
"""
@ -383,16 +428,18 @@ class TablespaceView(NodeView):
'name'
]
if did is not None:
SQL = render_template("/".join([self.template_path, 'properties.sql']),
did=did, conn=self.conn)
if tsid is not None:
SQL = render_template(
"/".join([self.template_path, 'properties.sql']),
tsid=tsid, conn=self.conn
)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
# Making copy of output for further processing
old_data = dict(res['rows'][0])
old_data = self._formatter(old_data, did)
old_data = self._formatter(old_data, tsid)
# To format privileges data coming from client
for key in ['spcacl']:
@ -410,28 +457,36 @@ class TablespaceView(NodeView):
if arg not in data:
data[arg] = old_data[arg]
SQL = render_template("/".join([self.template_path, 'update.sql']),
data=data, o_data=old_data)
SQL = render_template(
"/".join([self.template_path, 'update.sql']),
data=data, o_data=old_data
)
else:
# To format privileges coming from client
if 'spcacl' in data:
data['spcacl'] = parse_priv_to_db(data['spcacl'], 'TABLESPACE')
# If the request for new object which do not have did
SQL = render_template("/".join([self.template_path, 'create.sql']),
data=data)
# If the request for new object which do not have tsid
SQL = render_template(
"/".join([self.template_path, 'create.sql']),
data=data
)
SQL += "\n"
SQL += render_template("/".join([self.template_path, 'alter.sql']),
data=data)
SQL += render_template(
"/".join([self.template_path, 'alter.sql']),
data=data, conn=self.conn
)
return SQL
@check_precondition
def sql(self, gid, sid, did):
def sql(self, gid, sid, tsid):
"""
This function will generate sql for sql panel
"""
SQL = render_template("/".join([self.template_path, 'properties.sql']),
did=did, conn=self.conn)
SQL = render_template(
"/".join([self.template_path, 'properties.sql']),
tsid=tsid, conn=self.conn
)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
@ -439,7 +494,7 @@ class TablespaceView(NodeView):
# Making copy of output for future use
old_data = dict(res['rows'][0])
old_data = self._formatter(old_data, did)
old_data = self._formatter(old_data, tsid)
# To format privileges
if 'spcacl' in old_data:
@ -448,16 +503,20 @@ class TablespaceView(NodeView):
SQL = ''
# We are not showing create sql for system tablespace
if not old_data['name'].startswith('pg_'):
SQL = render_template("/".join([self.template_path, 'create.sql']),
data=old_data)
SQL = render_template(
"/".join([self.template_path, 'create.sql']),
data=old_data
)
SQL += "\n"
SQL += render_template("/".join([self.template_path, 'alter.sql']),
data=old_data)
SQL += render_template(
"/".join([self.template_path, 'alter.sql']),
data=old_data, conn=self.conn
)
sql_header = """
-- Tablespace: {0}
-- DROP TABLESPACE {0}
-- DROP TABLESPACE {0};
""".format(old_data['name'])
@ -477,9 +536,11 @@ class TablespaceView(NodeView):
This function will return list of variables available for
table spaces.
"""
res = []
SQL = render_template("/".join([self.template_path, 'variables.sql']))
SQL = render_template(
"/".join([self.template_path, 'variables.sql'])
)
status, rset = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=rset)
@ -489,19 +550,23 @@ class TablespaceView(NodeView):
)
@check_precondition
def stats(self, gid, sid, did):
def statistics(self, gid, sid, tsid=None):
"""
This function will return data for statistics panel
"""
SQL = render_template("/".join([self.template_path, 'stats.sql']),
did=did)
SQL = render_template(
"/".join([self.template_path, 'stats.sql']),
tsid=tsid, conn=self.conn
)
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
# TODO:// Format & return output of stats call accordingly
# TODO:// for now it's hardcoded as below
result = 'Tablespace Size: {0}'.format(res)
return ajax_response(response=result)

View File

@ -11,18 +11,18 @@ FROM
FROM
(SELECT ts.spcacl
FROM pg_tablespace ts
{% if did %}
WHERE ts.oid={{did}}::int
{% if tsid %}
WHERE ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
) acl,
(SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
aclexplode(ts.spcacl) as d FROM pg_tablespace ts
{% if did %}
WHERE ts.oid={{did}}::int
{% if tsid %}
WHERE ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
) a) 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
GROUP BY g.rolname, gt.rolname

View File

@ -37,5 +37,5 @@ COMMENT ON TABLESPACE {{ conn|qtIdent(data.name) }}
{% endif %}
{# ======== The SQl Below will fetch id for given dataspace ======== #}
{% if tablespace %}
SELECT ts.oid FROM pg_tablespace ts WHERE spcname = {{tablespace|qtLiteral}}
{% endif %}
SELECT ts.oid FROM pg_tablespace ts WHERE spcname = {{tablespace|qtLiteral}};
{% endif %}

View File

@ -1,9 +1,2 @@
{### SQL to delete tablespace object ###}
{% if did %}
SELECT spcname
FROM pg_tablespace ts
WHERE ts.oid = {{did}}
{% endif %}
{% if tsname %}
DROP TABLESPACE {{ conn|qtIdent(tsname) }};
{% endif %}

View File

@ -0,0 +1,9 @@
SELECT
ts.oid, spcname AS name, spcowner as owner
FROM
pg_tablespace ts
{% if tsid %}
WHERE
ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
ORDER BY name;

View File

@ -1,8 +1,11 @@
{### SQL to fetch tablespace object properties ###}
SELECT ts.oid, spcname AS name, spclocation, spcoptions, pg_get_userbyid(spcowner) as spcuser, spcacl,
pg_catalog.shobj_description(oid, 'pg_tablespace') AS description
FROM pg_tablespace ts
{% if did %}
WHERE ts.oid={{did}}::int
SELECT
ts.oid, spcname AS name, spclocation, spcoptions,
pg_get_userbyid(spcowner) as spcuser, spcacl,
pg_catalog.shobj_description(oid, 'pg_tablespace') AS description
FROM
pg_tablespace ts
{% if tsid %}
WHERE ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
ORDER BY name
ORDER BY name

View File

@ -1,2 +1,6 @@
{### SQL to fetch tablespace object stats ###}
SELECT pg_size_pretty(pg_tablespace_size({{did}})) AS tablespace_size
{% if tsid %}
SELECT pg_size_pretty(pg_tablespace_size({{ tsid|qtLiteral }}::OID)) AS size
{% else %}
SELECT ts.spcname as name, pg_size_pretty(pg_tablespace_size(ts.oid)) AS size FROM pg_catalog.pg_tablespace ts;
{% endif %}

View File

@ -11,18 +11,18 @@ FROM
FROM
(SELECT ts.spcacl
FROM pg_tablespace ts
{% if did %}
WHERE ts.oid={{did}}::int
{% if tsid %}
WHERE ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
) acl,
(SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
aclexplode(ts.spcacl) as d FROM pg_tablespace ts
{% if did %}
WHERE ts.oid={{did}}::int
{% if tsid %}
WHERE ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
) a) 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
GROUP BY g.rolname, gt.rolname

View File

@ -37,5 +37,5 @@ COMMENT ON TABLESPACE {{ conn|qtIdent(data.name) }}
{% endif %}
{# ======== The SQl Below will fetch id for given dataspace ======== #}
{% if tablespace %}
SELECT ts.oid FROM pg_tablespace ts WHERE spcname = {{tablespace|qtLiteral}}
{% endif %}
SELECT ts.oid FROM pg_tablespace ts WHERE spcname = {{tablespace|qtLiteral}};
{% endif %}

View File

@ -1,9 +1,2 @@
{### SQL to delete tablespace object ###}
{% if did %}
SELECT spcname
FROM pg_tablespace ts
WHERE ts.oid = {{did}}
{% endif %}
{% if tsname %}
DROP TABLESPACE {{ conn|qtIdent(tsname) }};
{% endif %}

View File

@ -0,0 +1,9 @@
SELECT
ts.oid AS oid, spcname AS name, spcowner as owner
FROM
pg_tablespace ts
{% if tsid %}
WHERE
ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
ORDER BY name;

View File

@ -1,10 +1,15 @@
{### SQL to fetch tablespace object properties ###}
SELECT ts.oid, spcname AS name, pg_catalog.pg_tablespace_location(ts.oid) AS spclocation, spcoptions,
pg_get_userbyid(spcowner) as spcuser, spcacl::text[],
pg_catalog.shobj_description(oid, 'pg_tablespace') AS description
,(SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=ts.oid) AS seclabels
FROM pg_tablespace ts
{% if did %}
WHERE ts.oid={{did}}::int
SELECT
ts.oid, spcname AS name, spcoptions, pg_get_userbyid(spcowner) as spcuser,
pg_catalog.pg_tablespace_location(ts.oid) AS spclocation, spcacl::text[],
pg_catalog.shobj_description(oid, 'pg_tablespace') AS description,
(SELECT
array_agg(provider || '=' || label)
FROM pg_shseclabel sl1
WHERE sl1.objoid=ts.oid) AS seclabels
FROM
pg_tablespace ts
{% if tsid %}
WHERE ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
ORDER BY name
ORDER BY name

View File

@ -1,2 +1,6 @@
{### SQL to fetch tablespace object stats ###}
SELECT pg_size_pretty(pg_tablespace_size({{did}})) AS tablespace_size
{% if tsid %}
SELECT pg_size_pretty(pg_tablespace_size({{ qtLiteral(tsid) }}::OID)) AS size
{% else %}
SELECT ts.spcname as name, pg_size_pretty(pg_tablespace_size(ts.oid)) AS size FROM pg_catalog.pg_tablespace ts;
{% endif %}

View File

@ -11,18 +11,18 @@ FROM
FROM
(SELECT ts.spcacl
FROM pg_tablespace ts
{% if did %}
WHERE ts.oid={{did}}::int
{% if tsid %}
WHERE ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
) acl,
(SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
aclexplode(ts.spcacl) as d FROM pg_tablespace ts
{% if did %}
WHERE ts.oid={{did}}::int
{% if tsid %}
WHERE ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
) a) 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
GROUP BY g.rolname, gt.rolname

View File

@ -37,5 +37,5 @@ COMMENT ON TABLESPACE {{ conn|qtIdent(data.name) }}
{% endif %}
{# ======== The SQl Below will fetch id for given dataspace ======== #}
{% if tablespace %}
SELECT ts.oid FROM pg_tablespace ts WHERE spcname = {{tablespace|qtLiteral}}
{% endif %}
SELECT ts.oid FROM pg_tablespace ts WHERE spcname = {{tablespace|qtLiteral}};
{% endif %}

View File

@ -1,9 +1,2 @@
{### SQL to delete tablespace object ###}
{% if did %}
SELECT spcname
FROM pg_tablespace ts
WHERE ts.oid = {{did}}
{% endif %}
{% if tsname %}
DROP TABLESPACE {{ conn|qtIdent(tsname) }};
{% endif %}

View File

@ -0,0 +1,8 @@
SELECT
ts.oid, spcname AS name, spcowner as owner
FROM pg_tablespace ts
{% if tsid %}
WHERE
ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
ORDER BY name;

View File

@ -2,7 +2,7 @@
SELECT ts.oid, spcname AS name, spclocation, spcoptions, pg_get_userbyid(spcowner) as spcuser, spcacl,
pg_catalog.shobj_description(oid, 'pg_tablespace') AS description
FROM pg_tablespace ts
{% if did %}
WHERE ts.oid={{did}}::int
{% if tsid %}
WHERE ts.oid={{ tsid|qtLiteral }}::OID
{% endif %}
ORDER BY name
ORDER BY name

View File

@ -1,2 +1,6 @@
{### SQL to fetch tablespace object stats ###}
SELECT pg_size_pretty(pg_tablespace_size({{did}})) AS tablespace_size
{% if tsid %}
SELECT pg_size_pretty(pg_tablespace_size({{ tsid|qtLiteral }}::OID)) AS size
{% else %}
SELECT ts.spcname as name, pg_size_pretty(pg_tablespace_size(ts.oid)) AS size FROM pg_catalog.pg_tablespace ts;
{% endif %}

View File

@ -33,7 +33,7 @@ def parse_priv_from_db(db_privileges):
return acl
def parse_priv_to_db(str_privileges, object_type = None):
def parse_priv_to_db(str_privileges, allowed_acls = []):
"""
Common utility function to parse privileges before sending to database.
"""
@ -51,18 +51,22 @@ def parse_priv_to_db(str_privileges, object_type = None):
'U': 'USAGE',
'X': 'EXECUTE'
}
privileges_max_cnt = {
'DATABASE': 3,
'TABLESPACE': 2,
'SCHEMA': 2
}
privileges = []
allowed_acls_len = len(allowed_acls)
for priv in str_privileges:
priv_with_grant = []
priv_without_grant = []
for privilege in priv['privileges']:
if privilege['privilege_type'] not in db_privileges:
continue
if privilege['privilege_type'] not in allowed_acls:
continue
if privilege['with_grant']:
priv_with_grant.append(
db_privileges[privilege['privilege_type']]
@ -72,19 +76,15 @@ def parse_priv_to_db(str_privileges, object_type = None):
db_privileges[privilege['privilege_type']]
)
if object_type in ("DATABASE", "SCHEMA", "TABLESPACE"):
priv_with_grant = ", ".join(priv_with_grant) if (
len(priv_with_grant) < privileges_max_cnt[object_type.upper()]
) else 'ALL'
priv_without_grant = ", ".join(priv_without_grant) if (
len(priv_without_grant) < privileges_max_cnt[object_type.upper()]
) else 'ALL'
else:
priv_with_grant = ", ".join(priv_with_grant)
priv_without_grant = ", ".join(priv_without_grant)
priv_with_grant = ", ".join(priv_with_grant) \
if len(priv_with_grant) < allowed_acls_len else 'ALL'
priv_without_grant = ", ".join(priv_without_grant) \
if len(priv_without_grant) < allowed_acls_len else 'ALL'
privileges.append({'grantee': priv['grantee'],
'with_grant': priv_with_grant,
'without_grant': priv_without_grant})
privileges.append({
'grantee': priv['grantee'],
'with_grant': priv_with_grant,
'without_grant': priv_without_grant
})
return privileges

View File

@ -19,7 +19,7 @@ from flask.ext.security import current_user
from ..abstract import BaseDriver, BaseConnection
from pgadmin.settings.settings_model import Server, User
from pgadmin.utils.crypto import encrypt, decrypt
from pgadmin.utils.crypto import decrypt
import random
from .keywords import ScanKeyword
@ -453,7 +453,7 @@ Attempt to reconnect it failed with the below error:
pg_conn = psycopg2.connect(
host=mgr.host,
port=mgr.port,
database=db,
database=self.db,
user=mgr.user,
password=password
)
@ -912,6 +912,7 @@ class Driver(BaseDriver):
if type(val) == list:
return map(lambda w: Driver.qtIdent(conn, w), val)
val = str(val)
if len(val) == 0:
continue