Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 61 additions & 42 deletions packages/django-cf/django_cf/db/base_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,63 +277,41 @@ def from_object(
return instance


class CFDatabase:
def __init__(self, database_wrapper):
self.databaseWrapper = database_wrapper

DataError = DataError

OperationalError = OperationalError

IntegrityError = IntegrityError

InternalError = InternalError

ProgrammingError = ProgrammingError

NotSupportedError = NotSupportedError
DatabaseError = DatabaseError
InterfaceError = InterfaceError
Error = Error

_defer_foreign_keys = False
class CFCursor:
def __init__(self, database):
self.database = database
self.lastResult: CFResult | None = None

lastResult: CFResult = None
@property
def _defer_foreign_keys(self):
return self.database._defer_foreign_keys

def defer_foreign_keys(self, state):
self._defer_foreign_keys = state

@staticmethod
def connect(binding):
return CFDatabase(binding)

def cursor(self):
return self

def commit(self):
return # No commits allowed

def rollback(self):
return # No commits allowed
self.database.defer_foreign_keys(state)

def fetchone(self):
return self.lastResult.fetchone()
result = self.lastResult
return result.fetchone()

def fetchall(self):
return self.lastResult.fetchall()
result = self.lastResult
return result.fetchall()

def fetchmany(self, size=1):
return self.lastResult.fetchmany(size)
result = self.lastResult
return result.fetchmany(size)

@property
def lastrowid(self):
return self.lastResult.lastrowid
result = self.lastResult
return result.lastrowid

@property
def rowcount(self):
return self.lastResult.rowcount
result = self.lastResult
return result.rowcount

def execute(self, query, params=None) -> None:
def execute(self, query, params=None):
from decimal import Decimal

# Transform django_date_trunc function calls to SQLite equivalents
Expand All @@ -353,14 +331,55 @@ def execute(self, query, params=None) -> None:

params = tuple(newParams)

self.lastResult = self.databaseWrapper.run_query(query, params)
self.lastResult = self.database.databaseWrapper.run_query(query, params)

return self

def close(self):
return


class CFDatabase:
def __init__(self, database_wrapper):
self.databaseWrapper = database_wrapper

DataError = DataError

OperationalError = OperationalError

IntegrityError = IntegrityError

InternalError = InternalError

ProgrammingError = ProgrammingError

NotSupportedError = NotSupportedError
DatabaseError = DatabaseError
InterfaceError = InterfaceError
Error = Error

_defer_foreign_keys = False

def defer_foreign_keys(self, state):
self._defer_foreign_keys = state

@staticmethod
def connect(binding):
return CFDatabase(binding)

def cursor(self):
return CFCursor(self)

def commit(self):
return # No commits allowed

def rollback(self):
return # No commits allowed

def close(self):
return


def is_read_only_query(query: str) -> bool:
parsed = sqlparse.parse(query.strip())

Expand Down
102 changes: 80 additions & 22 deletions packages/django-cf/tests/in_worker/worker/src/test_base_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,11 +327,18 @@ def test_connect(self):

assert db.databaseWrapper == mock_wrapper

def test_cursor_returns_self(self):
from django_cf.db.base_engine import CFDatabase
def test_cursor_returns_fresh_cursor_each_call(self):
from django_cf.db.base_engine import CFCursor, CFDatabase

db = CFDatabase(MagicMock())
assert db.cursor() is db
first_cursor = db.cursor()
second_cursor = db.cursor()

assert isinstance(first_cursor, CFCursor)
assert isinstance(second_cursor, CFCursor)
assert first_cursor is not second_cursor
assert first_cursor.database is db
assert second_cursor.database is db

def test_commit_does_nothing(self):
from django_cf.db.base_engine import CFDatabase
Expand All @@ -357,14 +364,29 @@ def test_defer_foreign_keys(self):
db.defer_foreign_keys(False)
assert db._defer_foreign_keys is False

def test_cursor_defer_foreign_keys_proxies_database_state(self):
from django_cf.db.base_engine import CFDatabase

db = CFDatabase(MagicMock())
first_cursor = db.cursor()
second_cursor = db.cursor()

first_cursor.defer_foreign_keys(True)
assert db._defer_foreign_keys is True
assert second_cursor._defer_foreign_keys is True

second_cursor.defer_foreign_keys(False)
assert db._defer_foreign_keys is False
assert first_cursor._defer_foreign_keys is False

def test_execute_converts_boolean_true(self):
from django_cf.db.base_engine import CFDatabase, CFResult

mock_wrapper = MagicMock()
mock_wrapper.run_query.return_value = CFResult([])
db = CFDatabase(mock_wrapper)
cursor = CFDatabase(mock_wrapper).cursor()

db.execute("INSERT INTO test VALUES (%s)", (True,))
cursor.execute("INSERT INTO test VALUES (%s)", (True,))

assert mock_wrapper.run_query.call_args[0][1] == (1,)

Expand All @@ -373,9 +395,9 @@ def test_execute_converts_boolean_false(self):

mock_wrapper = MagicMock()
mock_wrapper.run_query.return_value = CFResult([])
db = CFDatabase(mock_wrapper)
cursor = CFDatabase(mock_wrapper).cursor()

db.execute("INSERT INTO test VALUES (%s)", (False,))
cursor.execute("INSERT INTO test VALUES (%s)", (False,))

assert mock_wrapper.run_query.call_args[0][1] == (0,)

Expand All @@ -384,9 +406,9 @@ def test_execute_converts_decimal_to_string(self):

mock_wrapper = MagicMock()
mock_wrapper.run_query.return_value = CFResult([])
db = CFDatabase(mock_wrapper)
cursor = CFDatabase(mock_wrapper).cursor()

db.execute("INSERT INTO test VALUES (%s)", (Decimal("10.5"),))
cursor.execute("INSERT INTO test VALUES (%s)", (Decimal("10.5"),))

assert mock_wrapper.run_query.call_args[0][1] == ("10.5",)

Expand All @@ -395,33 +417,57 @@ def test_execute_no_params(self):

mock_wrapper = MagicMock()
mock_wrapper.run_query.return_value = CFResult([])
db = CFDatabase(mock_wrapper)
cursor = CFDatabase(mock_wrapper).cursor()

db.execute("SELECT * FROM test")
cursor.execute("SELECT * FROM test")

assert mock_wrapper.run_query.call_args[0] == ("SELECT * FROM test", None)

def test_execute_returns_cursor(self):
from django_cf.db.base_engine import CFDatabase, CFResult

mock_wrapper = MagicMock()
mock_wrapper.run_query.return_value = CFResult([])
cursor = CFDatabase(mock_wrapper).cursor()

assert cursor.execute("SELECT * FROM test") is cursor

def test_fetchone_delegates_to_result(self):
from django_cf.db.base_engine import CFDatabase, CFResult

mock_wrapper = MagicMock()
mock_wrapper.run_query.return_value = CFResult([(1, "test")])
db = CFDatabase(mock_wrapper)
cursor = CFDatabase(mock_wrapper).cursor()

db.execute("SELECT * FROM test")
cursor.execute("SELECT * FROM test")

assert db.fetchone() == (1, "test")
assert cursor.fetchone() == (1, "test")

def test_fetchall_delegates_to_result(self):
from django_cf.db.base_engine import CFDatabase, CFResult

mock_wrapper = MagicMock()
mock_wrapper.run_query.return_value = CFResult([(1, "a"), (2, "b")])
cursor = CFDatabase(mock_wrapper).cursor()

cursor.execute("SELECT * FROM test")

assert len(cursor.fetchall()) == 2

def test_cursors_keep_independent_results(self):
from django_cf.db.base_engine import CFDatabase, CFResult

mock_wrapper = MagicMock()
mock_wrapper.run_query.side_effect = [CFResult([(1,), (2,)]), CFResult([(9,)])]
db = CFDatabase(mock_wrapper)
outer_cursor = db.cursor()
inner_cursor = db.cursor()

db.execute("SELECT * FROM test")
outer_cursor.execute("SELECT * FROM outer_table")
inner_cursor.execute("SELECT * FROM inner_table")

assert len(db.fetchall()) == 2
assert inner_cursor.fetchone() == (9,)
assert sorted(outer_cursor.fetchall()) == [(1,), (2,)]

def test_lastrowid_property(self):
from django_cf.db.base_engine import CFDatabase, CFResult
Expand All @@ -430,11 +476,11 @@ def test_lastrowid_property(self):
mock_result = CFResult([])
mock_result.set_lastrowid(42)
mock_wrapper.run_query.return_value = mock_result
db = CFDatabase(mock_wrapper)
cursor = CFDatabase(mock_wrapper).cursor()

db.execute("INSERT INTO test VALUES (1)")
cursor.execute("INSERT INTO test VALUES (1)")

assert db.lastrowid == 42
assert cursor.lastrowid == 42

def test_rowcount_property(self):
from django_cf.db.base_engine import CFDatabase, CFResult
Expand All @@ -443,11 +489,23 @@ def test_rowcount_property(self):
mock_result = CFResult([])
mock_result.set_rowcount(5)
mock_wrapper.run_query.return_value = mock_result
db = CFDatabase(mock_wrapper)
cursor = CFDatabase(mock_wrapper).cursor()

cursor.execute('UPDATE test SET name = "new"')

assert cursor.rowcount == 5

def test_cursor_close_is_noop_and_keeps_result(self):
from django_cf.db.base_engine import CFDatabase, CFResult

mock_wrapper = MagicMock()
mock_wrapper.run_query.return_value = CFResult([(1,), (2,)])
cursor = CFDatabase(mock_wrapper).cursor()

db.execute('UPDATE test SET name = "new"')
cursor.execute("SELECT * FROM test")

assert db.rowcount == 5
assert cursor.close() is None
assert sorted(cursor.fetchall()) == [(1,), (2,)]


class TestCFDatabaseFeatures:
Expand Down
Loading
Loading