summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandre Fayolle <alexandre.fayolle@logilab.fr>2010-01-20 09:19:20 +0100
committerAlexandre Fayolle <alexandre.fayolle@logilab.fr>2010-01-20 09:19:20 +0100
commited82e1854ffa05655a24bdb1637e7115df8a1c8c (patch)
treedc5f8409796b0daa81637acfb4e984235d3e4d4d
parent0fa7d0581ce27b6a07d3c87492cb5b509c1b907e (diff)
downloadlogilab-common-ed82e1854ffa05655a24bdb1637e7115df8a1c8c.tar.gz
enable support for backup and restore operations in SQL Server (ticket 19687)
-rw-r--r--adbh.py60
-rw-r--r--db.py14
2 files changed, 74 insertions, 0 deletions
diff --git a/adbh.py b/adbh.py
index 9537ad8..1dbb979 100644
--- a/adbh.py
+++ b/adbh.py
@@ -16,6 +16,8 @@ Helpers are provided for postgresql, mysql and sqlite.
"""
__docformat__ = "restructuredtext en"
+import os
+import sys
class BadQuery(Exception): pass
class UnsupportedFunction(BadQuery): pass
@@ -175,6 +177,7 @@ class _GenericAdvFuncHelper:
"""return a list of commands to restore a backup of the given database"""
raise NotImplementedError('not supported by this DBMS')
+
# helpers to standardize SQL according to the database
def sql_current_date(self):
@@ -595,6 +598,59 @@ class _SqlServer2005FuncHelper(_GenericAdvFuncHelper):
return StringIO.StringIO(value)
+ def backup_command(self, dbname, dbhost, dbuser, backupfile,
+ keepownership=True):
+ """return a command to backup the given database"""
+ return [sys.executable, os.path.normpath(__file__),
+ "_SqlServer2005FuncHelper._do_backup", dbhost, dbname, backupfile]
+
+ def restore_commands(self, dbname, dbhost, dbuser, backupfile,
+ encoding='utf-8', keepownership=True, drop=True):
+ """return a list of commands to restore a backup of the given database"""
+ return [[sys.executable, os.path.normpath(__file__),
+ "_SqlServer2005FuncHelper._do_restore", dbhost, dbname, backupfile],
+ ]
+
+ @staticmethod
+ def _do_backup():
+ import time
+ from logilab.common.db import get_connection
+ dbhost = sys.argv[2]
+ dbname = sys.argv[3]
+ filename = sys.argv[4]
+ cnx = get_connection(driver='sqlserver2005', host=dbhost, database=dbname, extra_args='autocommit;trusted_connection')
+ cursor = cnx.cursor()
+ cursor.execute("BACKUP DATABASE ? TO DISK= ? ", (dbname, filename,))
+ prev_size = -1
+ err_count = 0
+ same_size_count = 0
+ while err_count < 10 and same_size_count < 10:
+ time.sleep(1)
+ try:
+ size = os.path.getsize(filename)
+ except OSError, exc:
+ err_count +=1
+ print exc
+ if size > prev_size:
+ same_size_count = 0
+ prev_size = size
+ else:
+ same_size_count += 1
+ cnx.close()
+ sys.exit(0)
+
+ @staticmethod
+ def _do_restore():
+ """return the SQL statement to restore a backup of the given database"""
+ from logilab.common.db import get_connection
+ dbhost = sys.argv[2]
+ dbname = sys.argv[3]
+ filename = sys.argv[4]
+ cnx = get_connection(driver='sqlserver2005', host=dbhost, database='master', extra_args='autocommit;trusted_connection')
+ cursor = cnx.cursor()
+ cursor.execute("RESTORE DATABASE ? FROM DISK= ? WITH REPLACE", (dbname, filename,))
+ sys.exit(0)
+
ADV_FUNC_HELPER_DIRECTORY = {'postgres': _PGAdvFuncHelper(),
'sqlite': _SqliteAdvFuncHelper(),
'mysql': _MyAdvFuncHelper(),
@@ -616,3 +672,7 @@ def auto_register_function(funcdef):
"""register the function `funcdef` on supported backends"""
for driver in funcdef.supported_backends:
register_function(driver, funcdef)
+
+if __name__ == "__main__": # used to backup sql server db
+ func_call = sys.argv[1]
+ eval(func_call+'()')
diff --git a/db.py b/db.py
index 45ff04c..c51190d 100644
--- a/db.py
+++ b/db.py
@@ -604,6 +604,7 @@ class _MySqlDBAdapter(DBAPIAdapter):
class _BaseSqlServerAdapter(DBAPIAdapter):
driver = 'Override in subclass'
_use_trusted_connection = False
+ _use_autocommit = False
_fetch_lock = threading.Lock()
@classmethod
@@ -613,11 +614,22 @@ class _BaseSqlServerAdapter(DBAPIAdapter):
Authentication (i.e. passwordless auth)
"""
klass._use_trusted_connection = use_trusted
+
+ @classmethod
+ def use_autocommit(klass, use_autocommit=False):
+ """
+ pass True to this class method to enable autocommit (required
+ for backup and restore)
+ """
+ klass._use_autocommit = use_autocommit
+
@classmethod
def _process_extra_args(klass, arguments):
arguments = arguments.lower().split(';')
if 'trusted_connection' in arguments:
klass.use_trusted_connection(True)
+ if 'autocommit' in arguments:
+ klass.use_autocommit(True)
def connect(self, host='', database='', user='', password='', port=None, extra_args=None):
"""Handles pyodbc connection format
@@ -750,6 +762,8 @@ class _PyodbcAdapter(_BaseSqlServerAdapter):
variables['Trusted_Connection'] = 'yes'
del variables['user']
del variables['password']
+ if self._use_autocommit:
+ variables['autocommit'] = True
return self._native_module.connect(**variables)
class _PyodbcSqlServer2000Adapter(_PyodbcAdapter):