Skip to content

Commit dd42d5e

Browse files
committed
Merge pull request pymssql#267 from pymssql/pr210
PR pymssql#210 ready for merge.
2 parents a7797ca + acab054 commit dd42d5e

3 files changed

Lines changed: 100 additions & 10 deletions

File tree

pymssql.pyx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,16 @@ cdef class Connection:
235235
raise InterfaceError('Connection is closed.')
236236
return self.conn
237237

238-
def __init__(self, conn, as_dict):
238+
def __init__(self, conn, as_dict, autocommit):
239239
self.conn = conn
240-
self._autocommit = False
240+
self._autocommit = autocommit
241241
self.as_dict = as_dict
242-
try:
243-
self._conn.execute_non_query('BEGIN TRAN')
244-
except Exception, e:
245-
raise OperationalError('Cannot start transaction: ' + str(e.args[0]))
242+
243+
if not autocommit:
244+
try:
245+
self._conn.execute_non_query('BEGIN TRAN')
246+
except Exception, e:
247+
raise OperationalError('Cannot start transaction: ' + str(e.args[0]))
246248

247249
def __dealloc__(self):
248250
if self.conn:
@@ -258,6 +260,7 @@ cdef class Connection:
258260

259261
tran_type = 'ROLLBACK' if status else 'BEGIN'
260262
self._conn.execute_non_query('%s TRAN' % tran_type)
263+
261264
self._autocommit = status
262265

263266
def __enter__(self):
@@ -574,7 +577,7 @@ cdef class Cursor:
574577

575578
def connect(server='.', user='', password='', database='', timeout=0,
576579
login_timeout=60, charset='UTF-8', as_dict=False,
577-
host='', appname=None, port='1433', conn_properties=None):
580+
host='', appname=None, port='1433', conn_properties=None, autocommit=False):
578581
"""
579582
Constructor for creating a connection to the database. Returns a
580583
Connection object.
@@ -602,6 +605,8 @@ def connect(server='.', user='', password='', database='', timeout=0,
602605
:keyword conn_properties: SQL queries to send to the server upon connection
603606
establishment. Can be a string or another kind
604607
of iterable of strings
608+
:keyword autocommit whether to use default autocommiting mode or not
609+
:type autocommit: boolean
605610
"""
606611

607612
_mssql.login_timeout = login_timeout
@@ -637,7 +642,7 @@ def connect(server='.', user='', password='', database='', timeout=0,
637642
if timeout != 0:
638643
conn.query_timeout = timeout
639644

640-
return Connection(conn, as_dict)
645+
return Connection(conn, as_dict, autocommit)
641646

642647
def get_max_connections():
643648
"""

tests/helpers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@ def mssqlconn(conn_properties=None):
3939
)
4040

4141

42-
def pymssqlconn():
42+
def pymssqlconn(**kwargs):
4343
return pymssql.connect(
4444
server=config.server,
4545
user=config.user,
4646
password=config.password,
4747
database=config.database,
4848
port=config.port,
49+
**kwargs
4950
)
5051

5152

tests/test_pymssql.py

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import unittest
22

3+
import pytest
4+
35
import pymssql as pym
46

5-
from .helpers import pymssqlconn, PyTableBase, drop_table, CursorBase, eq_, config
7+
from .helpers import (pymssqlconn, PyTableBase, CursorBase, eq_, config,
8+
skip_test)
69

710
class TestDBAPI2(object):
811
def test_version(self):
@@ -83,13 +86,15 @@ def test_rollback_after_create_error(self):
8386
# too
8487
eq_(self.row_count(), 0)
8588

89+
8690
class TestCursor(CursorBase):
8791
dbmod = pym
8892

8993
@classmethod
9094
def newconn(cls):
9195
cls.conn = pymssqlconn()
9296

97+
9398
class TestBasicConnection(unittest.TestCase):
9499

95100
def connect(self, conn_props=None):
@@ -130,3 +135,82 @@ def test_conn_props_override(self):
130135
password=config.password
131136
)
132137
conn.close()
138+
139+
140+
class TestAutocommit(unittest.TestCase, PyTableBase):
141+
tname = 'test'
142+
cols = (
143+
'name varchar(50)',
144+
)
145+
146+
insert_query = 'INSERT INTO {tname} VALUES (%s)'.format(tname=tname)
147+
select_query = 'SELECT * FROM {tname} WHERE name = (%s)'.format(tname=tname)
148+
149+
test_db_name = 'autocommit_test_database'
150+
151+
def setUp(self):
152+
PyTableBase.setUp(self)
153+
154+
def tearDown(self):
155+
self.conn._conn.execute_non_query("IF EXISTS(select * from sys.databases where name='{0}') DROP DATABASE {0}".format(self.test_db_name))
156+
157+
def test_db_creation_with_autocommit(self):
158+
"""
159+
Try creating and dropping database with autocommit
160+
"""
161+
cur = pymssqlconn(autocommit=True).cursor()
162+
try:
163+
cur.execute("CREATE DATABASE {0}".format(self.test_db_name))
164+
except pym.OperationalError as e:
165+
expected_msg = "CREATE DATABASE permission denied in database 'master'"
166+
if expected_msg in str(e.args[1]):
167+
skip_test('We have no CREATE DATABASE permission on test database')
168+
else:
169+
pytest.fail()
170+
else:
171+
cur.execute("DROP DATABASE {0}".format(self.test_db_name))
172+
173+
def test_db_creation_without_autocommit(self):
174+
"""
175+
Try creating and dropping database without autocommit, expecting it to fail
176+
"""
177+
cur = pymssqlconn(autocommit=False).cursor()
178+
with pytest.raises(pym.OperationalError) as excinfo:
179+
cur.execute("CREATE DATABASE autocommit_test_database")
180+
expected_msg = "CREATE DATABASE statement not allowed within multi-statement transaction"
181+
assert expected_msg in excinfo.exconly()
182+
183+
def test_autocommit_flipping_tf(self):
184+
insert_value = 'true-false'
185+
conn = pymssqlconn(autocommit=True)
186+
conn.autocommit(False)
187+
cur = conn.cursor()
188+
cur.execute(self.insert_query, insert_value)
189+
conn.commit()
190+
cur.execute(self.select_query, insert_value)
191+
row = cur.fetchone()
192+
cur.close()
193+
conn.close()
194+
assert len(row) > 0
195+
196+
def test_autocommit_flipping_ft(self):
197+
insert_value = 'false-true'
198+
conn = pymssqlconn(autocommit=False)
199+
conn.autocommit(True)
200+
cur = conn.cursor()
201+
cur.execute(self.insert_query, insert_value)
202+
cur.execute(self.select_query, insert_value)
203+
row = cur.fetchone()
204+
assert len(row) > 0
205+
206+
def test_autocommit_false_does_not_commit(self):
207+
insert_value = 'false'
208+
conn = pymssqlconn(autocommit=False)
209+
cur = conn.cursor()
210+
cur.execute(self.insert_query, insert_value)
211+
conn.rollback()
212+
cur.execute(self.select_query, insert_value)
213+
row = cur.fetchone()
214+
cur.close()
215+
conn.close()
216+
assert row is None

0 commit comments

Comments
 (0)