Skip to content
This repository has been archived by the owner on Apr 17, 2019. It is now read-only.

Commit

Permalink
Close #26
Browse files Browse the repository at this point in the history
  • Loading branch information
megaponchic committed Aug 28, 2015
1 parent 932bdad commit 01964eb
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 65 deletions.
2 changes: 1 addition & 1 deletion pgpm/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.1.35'
__version__ = '0.1.41'
11 changes: 6 additions & 5 deletions pgpm/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@
GRANT EXECUTE on all future and current functions
GRANT USAGE, SELECT on all current and future sequences
In case used with deploy command the following will be applied:
GRANT SELECT, INSERT, UPDATE, DELETE on all current tables
GRANT USAGE on the schema
GRANT EXECUTE on all current functions
GRANT USAGE, SELECT on all current sequences
-m <mode>, --mode <mode> Deployment mode. Can be:
* safe. Add constraints to deployment. Will not deploy schema
if it already exists in the DB
Expand Down Expand Up @@ -95,7 +94,7 @@

from collections import OrderedDict

from pgpm.utils import config, vcs
from pgpm.utils import config, vcs, db

from pgpm import _version, _variables
from docopt import docopt
Expand All @@ -111,7 +110,8 @@
"GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA {0} TO {1};" \
"GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA {0} TO {1};"

GRANT_USAGE_PRIVILEGES = "GRANT USAGE ON SCHEMA {0} TO {1}"
GRANT_USAGE_PRIVILEGES = "GRANT USAGE ON SCHEMA {0} TO {1};" \
"GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA {0} TO {1};"

# getting logging
logger = logging.getLogger(__name__)
Expand All @@ -122,7 +122,8 @@ def connect_db(connection_string):
Connect to DB or exit on exception
"""
logger.info('Connecting to databases for deployment...')
conn = psycopg2.connect(connection_string)
conn = psycopg2.connect(connection_string, connection_factory=db.MegaConnection)
conn.init(logger)
cur = conn.cursor()
logger.info('Connected to {0}'.format(connection_string))

Expand Down
10 changes: 9 additions & 1 deletion pgpm/scripts/functions/_alter_schema_owner.sql
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ $BODY$
--
---
DECLARE
l_schema TEXT;
l_functions TEXT;
l_tables TEXT;
l_sequences TEXT;
Expand All @@ -22,6 +23,13 @@ DECLARE
l_types TEXT;

BEGIN

l_schema := 'ALTER SCHEMA '
|| quote_ident(p_schema)
|| ' OWNER TO '
|| quote_ident(p_owner)
|| ';';

SELECT string_agg('ALTER FUNCTION '
|| quote_ident(n.nspname) || '.'
|| quote_ident(p.proname) || '('
Expand Down Expand Up @@ -78,7 +86,7 @@ BEGIN
l_types := '';
END IF;

EXECUTE l_functions || l_tables || l_sequences || l_views || l_domains || l_triggers || l_types;
EXECUTE l_schema || l_functions || l_tables || l_sequences || l_views || l_domains || l_triggers || l_types;

END;
$BODY$
Expand Down
105 changes: 47 additions & 58 deletions pgpm/utils/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,61 @@
import psycopg2.extensions
import logging

# class

class ConnectionManager:
class MegaConnection(psycopg2.extensions.connection):
"""
Simple DB connections manager. Acts as a pool with one connection
A connection that uses `MegaCursor` automatically.
"""

def __init__(self, connection_string, logger=None):
"""
Initializes connection to DB
"""
conn = psycopg2.connect(connection_string)
self._connection_string = connection_string
self._conn = conn
self.logger = logger or logging.getLogger(__name__)

def get_connection_db(self):
"""
Connect to DB or exit on exception
"""
if not self._conn or self._conn.closed != 0:
conn = psycopg2.connect(self._connection_string)
self._conn = conn
return conn
def __init__(self, dsn, *more):
super().__init__(dsn, *more)
self._last_notice_flushed_index = -1
self.logger = logging.getLogger(__name__)

def cursor(self, *args, **kwargs):
kwargs.setdefault('cursor_factory', MegaCursor)
return super(MegaConnection, self).cursor(*args, **kwargs)

def fetch_new_notices(self):
if len(self.notices) > self._last_notice_flushed_index + 1:
unflushed_notices = self.notices[self._last_notice_flushed_index + 1:len(self.notices)]
self._last_notice_flushed_index = len(self.notices) - 1
return unflushed_notices
else:
return self._conn

def close_connection_db(self):
"""
Close DB connection and cursor
"""
if not self._conn:
return None
else:
return self._conn.close()

def call_stored_procedure_safely(self, name, arguments_list=None, commit=True):
"""
Calls pg stored procedure and returns cursor.
Wrapper also rollbacks on exception and logs activity
:param name: name of procedure
:param arguments_list: arguments
:return: cursor
def init(self, logger):
"""Initialize the connection to log to `!logger`.
The `!logger` parameter is a Logger
instance from the standard logging module.
"""
conn = self.get_connection_db()
cur = conn.cursor()
try:
if arguments_list:
cur.callproc(name, arguments_list)
self.logger.debug('Stored procedure {0} with arguments {1} called'.format(name, arguments_list))
else:
cur.callproc(name)
self.logger.debug('Stored procedure {0} called'.format(name))
if commit:
conn.commit()
except psycopg2.InternalError:
conn.close()
conn = self.get_connection_db()
self.logger.exception('Database error')
pass
except:
conn.rollback()
self.logger.exception('Database error')
pass
self.logger = logger or self.logger

return cur

class MegaCursor(psycopg2.extensions.cursor):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.connection.__class__.__name__ != 'MegaConnection':
raise self.connection.ProgrammingError(
'MegaCursor can only be used with MegaConnection. Instead type "{0}" is used. '
'Reinitialise db connection with correct class'.format(self.connection.__class__.__name__))

def execute(self, query, args=None):
try:
return super(MegaCursor, self).execute(query, args)
finally:
self.connection.logger.debug(self.query.decode('utf-8'))
noticies = self.connection.fetch_new_notices()
if noticies:
for notice in noticies:
self.connection.logger.debug(notice)

def callproc(self, procname, args=None):
try:
return super(MegaCursor, self).callproc(procname, args)
finally:
self.connection.logger.debug(self.query.decode('utf-8'))
noticies = self.connection.fetch_new_notices()
if noticies:
for notice in noticies:
self.connection.logger.debug(notice)

0 comments on commit 01964eb

Please sign in to comment.