123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806 |
- # Copyright 2014-2016 OpenMarket Ltd
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- from collections import OrderedDict
- from typing import Generator
- from unittest.mock import Mock, call, patch
- from twisted.internet import defer
- from synapse.storage._base import SQLBaseStore
- from synapse.storage.database import DatabasePool
- from synapse.storage.engines import create_engine
- from tests import unittest
- from tests.server import TestHomeServer
- from tests.utils import USE_POSTGRES_FOR_TESTS, default_config
- class SQLBaseStoreTestCase(unittest.TestCase):
- """Test the "simple" SQL generating methods in SQLBaseStore."""
- def setUp(self) -> None:
- # This is the Twisted connection pool.
- conn_pool = Mock(spec=["runInteraction", "runWithConnection"])
- self.mock_txn = Mock()
- if USE_POSTGRES_FOR_TESTS:
- # To avoid testing psycopg2 itself, patch execute_batch/execute_values
- # to assert how it is called.
- from psycopg2 import extras
- self.mock_execute_batch = Mock()
- self.execute_batch_patcher = patch.object(
- extras, "execute_batch", new=self.mock_execute_batch
- )
- self.execute_batch_patcher.start()
- self.mock_execute_values = Mock()
- self.execute_values_patcher = patch.object(
- extras, "execute_values", new=self.mock_execute_values
- )
- self.execute_values_patcher.start()
- self.mock_conn = Mock(
- spec_set=[
- "cursor",
- "rollback",
- "commit",
- "closed",
- "reconnect",
- "set_session",
- "encoding",
- ]
- )
- self.mock_conn.encoding = "UNICODE"
- else:
- self.mock_conn = Mock(spec_set=["cursor", "rollback", "commit"])
- self.mock_conn.cursor.return_value = self.mock_txn
- self.mock_txn.connection = self.mock_conn
- self.mock_conn.rollback.return_value = None
- # Our fake runInteraction just runs synchronously inline
- def runInteraction(func, *args, **kwargs) -> defer.Deferred: # type: ignore[no-untyped-def]
- return defer.succeed(func(self.mock_txn, *args, **kwargs))
- conn_pool.runInteraction = runInteraction
- def runWithConnection(func, *args, **kwargs): # type: ignore[no-untyped-def]
- return defer.succeed(func(self.mock_conn, *args, **kwargs))
- conn_pool.runWithConnection = runWithConnection
- config = default_config(name="test", parse=True)
- hs = TestHomeServer("test", config=config)
- if USE_POSTGRES_FOR_TESTS:
- db_config = {"name": "psycopg2", "args": {}}
- else:
- db_config = {"name": "sqlite3"}
- engine = create_engine(db_config)
- fake_engine = Mock(wraps=engine)
- fake_engine.in_transaction.return_value = False
- fake_engine.module.OperationalError = engine.module.OperationalError
- fake_engine.module.DatabaseError = engine.module.DatabaseError
- fake_engine.module.IntegrityError = engine.module.IntegrityError
- # Don't convert param style to make assertions easier.
- fake_engine.convert_param_style = lambda sql: sql
- # To fix isinstance(...) checks.
- fake_engine.__class__ = engine.__class__ # type: ignore[assignment]
- db = DatabasePool(Mock(), Mock(config=db_config), fake_engine)
- db._db_pool = conn_pool
- self.datastore = SQLBaseStore(db, None, hs) # type: ignore[arg-type]
- def tearDown(self) -> None:
- if USE_POSTGRES_FOR_TESTS:
- self.execute_batch_patcher.stop()
- self.execute_values_patcher.stop()
- @defer.inlineCallbacks
- def test_insert_1col(self) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 1
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_insert(
- table="tablename", values={"columname": "Value"}
- )
- )
- self.mock_txn.execute.assert_called_once_with(
- "INSERT INTO tablename (columname) VALUES(?)", ("Value",)
- )
- @defer.inlineCallbacks
- def test_insert_3cols(self) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 1
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_insert(
- table="tablename",
- # Use OrderedDict() so we can assert on the SQL generated
- values=OrderedDict([("colA", 1), ("colB", 2), ("colC", 3)]),
- )
- )
- self.mock_txn.execute.assert_called_once_with(
- "INSERT INTO tablename (colA, colB, colC) VALUES(?, ?, ?)", (1, 2, 3)
- )
- @defer.inlineCallbacks
- def test_insert_many(self) -> Generator["defer.Deferred[object]", object, None]:
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_insert_many(
- table="tablename",
- keys=(
- "col1",
- "col2",
- ),
- values=[
- (
- "val1",
- "val2",
- ),
- ("val3", "val4"),
- ],
- desc="",
- )
- )
- if USE_POSTGRES_FOR_TESTS:
- self.mock_execute_values.assert_called_once_with(
- self.mock_txn,
- "INSERT INTO tablename (col1, col2) VALUES ?",
- [("val1", "val2"), ("val3", "val4")],
- template=None,
- fetch=False,
- )
- else:
- self.mock_txn.executemany.assert_called_once_with(
- "INSERT INTO tablename (col1, col2) VALUES(?, ?)",
- [("val1", "val2"), ("val3", "val4")],
- )
- @defer.inlineCallbacks
- def test_insert_many_no_iterable(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_insert_many(
- table="tablename",
- keys=(
- "col1",
- "col2",
- ),
- values=[],
- desc="",
- )
- )
- if USE_POSTGRES_FOR_TESTS:
- self.mock_execute_values.assert_not_called()
- else:
- self.mock_txn.executemany.assert_not_called()
- @defer.inlineCallbacks
- def test_select_one_1col(self) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 1
- self.mock_txn.__iter__ = Mock(return_value=iter([("Value",)]))
- value = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_select_one_onecol(
- table="tablename", keyvalues={"keycol": "TheKey"}, retcol="retcol"
- )
- )
- self.assertEqual("Value", value)
- self.mock_txn.execute.assert_called_once_with(
- "SELECT retcol FROM tablename WHERE keycol = ?", ["TheKey"]
- )
- @defer.inlineCallbacks
- def test_select_one_3col(self) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 1
- self.mock_txn.fetchone.return_value = (1, 2, 3)
- ret = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_select_one(
- table="tablename",
- keyvalues={"keycol": "TheKey"},
- retcols=["colA", "colB", "colC"],
- )
- )
- self.assertEqual((1, 2, 3), ret)
- self.mock_txn.execute.assert_called_once_with(
- "SELECT colA, colB, colC FROM tablename WHERE keycol = ?", ["TheKey"]
- )
- @defer.inlineCallbacks
- def test_select_one_missing(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 0
- self.mock_txn.fetchone.return_value = None
- ret = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_select_one(
- table="tablename",
- keyvalues={"keycol": "Not here"},
- retcols=["colA"],
- allow_none=True,
- )
- )
- self.assertIsNone(ret)
- @defer.inlineCallbacks
- def test_select_list(self) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 3
- self.mock_txn.fetchall.return_value = [(1,), (2,), (3,)]
- self.mock_txn.description = (("colA", None, None, None, None, None, None),)
- ret = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_select_list(
- table="tablename", keyvalues={"keycol": "A set"}, retcols=["colA"]
- )
- )
- self.assertEqual([(1,), (2,), (3,)], ret)
- self.mock_txn.execute.assert_called_once_with(
- "SELECT colA FROM tablename WHERE keycol = ?", ["A set"]
- )
- @defer.inlineCallbacks
- def test_select_many_batch(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 3
- self.mock_txn.fetchall.side_effect = [[(1,), (2,)], [(3,)]]
- ret = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_select_many_batch(
- table="tablename",
- column="col1",
- iterable=("val1", "val2", "val3"),
- retcols=("col2",),
- keyvalues={"col3": "val4"},
- batch_size=2,
- )
- )
- self.mock_txn.execute.assert_has_calls(
- [
- call(
- "SELECT col2 FROM tablename WHERE col1 = ANY(?) AND col3 = ?",
- [["val1", "val2"], "val4"],
- ),
- call(
- "SELECT col2 FROM tablename WHERE col1 = ANY(?) AND col3 = ?",
- [["val3"], "val4"],
- ),
- ],
- )
- self.assertEqual([(1,), (2,), (3,)], ret)
- def test_select_many_no_iterable(self) -> None:
- self.mock_txn.rowcount = 3
- self.mock_txn.fetchall.side_effect = [(1,), (2,)]
- ret = self.datastore.db_pool.simple_select_many_txn(
- self.mock_txn,
- table="tablename",
- column="col1",
- iterable=(),
- retcols=("col2",),
- keyvalues={"col3": "val4"},
- )
- self.mock_txn.execute.assert_not_called()
- self.assertEqual([], ret)
- @defer.inlineCallbacks
- def test_update_one_1col(self) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 1
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_update_one(
- table="tablename",
- keyvalues={"keycol": "TheKey"},
- updatevalues={"columnname": "New Value"},
- )
- )
- self.mock_txn.execute.assert_called_once_with(
- "UPDATE tablename SET columnname = ? WHERE keycol = ?",
- ["New Value", "TheKey"],
- )
- @defer.inlineCallbacks
- def test_update_one_4cols(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 1
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_update_one(
- table="tablename",
- keyvalues=OrderedDict([("colA", 1), ("colB", 2)]),
- updatevalues=OrderedDict([("colC", 3), ("colD", 4)]),
- )
- )
- self.mock_txn.execute.assert_called_once_with(
- "UPDATE tablename SET colC = ?, colD = ? WHERE" " colA = ? AND colB = ?",
- [3, 4, 1, 2],
- )
- @defer.inlineCallbacks
- def test_update_many(self) -> Generator["defer.Deferred[object]", object, None]:
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_update_many(
- table="tablename",
- key_names=("col1", "col2"),
- key_values=[("val1", "val2")],
- value_names=("col3",),
- value_values=[("val3",)],
- desc="",
- )
- )
- if USE_POSTGRES_FOR_TESTS:
- self.mock_execute_batch.assert_called_once_with(
- self.mock_txn,
- "UPDATE tablename SET col3 = ? WHERE col1 = ? AND col2 = ?",
- [("val3", "val1", "val2")],
- )
- else:
- self.mock_txn.executemany.assert_called_once_with(
- "UPDATE tablename SET col3 = ? WHERE col1 = ? AND col2 = ?",
- [("val3", "val1", "val2")],
- )
- # key_values and value_values must be the same length.
- with self.assertRaises(ValueError):
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_update_many(
- table="tablename",
- key_names=("col1", "col2"),
- key_values=[("val1", "val2")],
- value_names=("col3",),
- value_values=[],
- desc="",
- )
- )
- @defer.inlineCallbacks
- def test_update_many_no_iterable(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_update_many(
- table="tablename",
- key_names=("col1", "col2"),
- key_values=[],
- value_names=("col3",),
- value_values=[],
- desc="",
- )
- )
- if USE_POSTGRES_FOR_TESTS:
- self.mock_execute_batch.assert_not_called()
- else:
- self.mock_txn.executemany.assert_not_called()
- @defer.inlineCallbacks
- def test_delete_one(self) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 1
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_delete_one(
- table="tablename", keyvalues={"keycol": "Go away"}
- )
- )
- self.mock_txn.execute.assert_called_once_with(
- "DELETE FROM tablename WHERE keycol = ?", ["Go away"]
- )
- @defer.inlineCallbacks
- def test_delete_many(self) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 2
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_delete_many(
- table="tablename",
- column="col1",
- iterable=("val1", "val2"),
- keyvalues={"col2": "val3"},
- desc="",
- )
- )
- self.mock_txn.execute.assert_called_once_with(
- "DELETE FROM tablename WHERE col1 = ANY(?) AND col2 = ?",
- [["val1", "val2"], "val3"],
- )
- self.assertEqual(result, 2)
- @defer.inlineCallbacks
- def test_delete_many_no_iterable(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_delete_many(
- table="tablename",
- column="col1",
- iterable=(),
- keyvalues={"col2": "val3"},
- desc="",
- )
- )
- self.mock_txn.execute.assert_not_called()
- self.assertEqual(result, 0)
- @defer.inlineCallbacks
- def test_delete_many_no_keyvalues(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 2
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_delete_many(
- table="tablename",
- column="col1",
- iterable=("val1", "val2"),
- keyvalues={},
- desc="",
- )
- )
- self.mock_txn.execute.assert_called_once_with(
- "DELETE FROM tablename WHERE col1 = ANY(?)", [["val1", "val2"]]
- )
- self.assertEqual(result, 2)
- @defer.inlineCallbacks
- def test_upsert(self) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 1
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert(
- table="tablename",
- keyvalues={"columnname": "oldvalue"},
- values={"othercol": "newvalue"},
- )
- )
- self.mock_txn.execute.assert_called_once_with(
- "INSERT INTO tablename (columnname, othercol) VALUES (?, ?) ON CONFLICT (columnname) DO UPDATE SET othercol=EXCLUDED.othercol",
- ["oldvalue", "newvalue"],
- )
- self.assertTrue(result)
- @defer.inlineCallbacks
- def test_upsert_no_values(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 1
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert(
- table="tablename",
- keyvalues={"columnname": "value"},
- values={},
- insertion_values={"columnname": "value"},
- )
- )
- self.mock_txn.execute.assert_called_once_with(
- "INSERT INTO tablename (columnname) VALUES (?) ON CONFLICT (columnname) DO NOTHING",
- ["value"],
- )
- self.assertTrue(result)
- @defer.inlineCallbacks
- def test_upsert_with_insertion(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 1
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert(
- table="tablename",
- keyvalues={"columnname": "oldvalue"},
- values={"othercol": "newvalue"},
- insertion_values={"thirdcol": "insertionval"},
- )
- )
- self.mock_txn.execute.assert_called_once_with(
- "INSERT INTO tablename (columnname, thirdcol, othercol) VALUES (?, ?, ?) ON CONFLICT (columnname) DO UPDATE SET othercol=EXCLUDED.othercol",
- ["oldvalue", "insertionval", "newvalue"],
- )
- self.assertTrue(result)
- @defer.inlineCallbacks
- def test_upsert_with_where(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.mock_txn.rowcount = 1
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert(
- table="tablename",
- keyvalues={"columnname": "oldvalue"},
- values={"othercol": "newvalue"},
- where_clause="thirdcol IS NULL",
- )
- )
- self.mock_txn.execute.assert_called_once_with(
- "INSERT INTO tablename (columnname, othercol) VALUES (?, ?) ON CONFLICT (columnname) WHERE thirdcol IS NULL DO UPDATE SET othercol=EXCLUDED.othercol",
- ["oldvalue", "newvalue"],
- )
- self.assertTrue(result)
- @defer.inlineCallbacks
- def test_upsert_many(self) -> Generator["defer.Deferred[object]", object, None]:
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert_many(
- table="tablename",
- key_names=["keycol1", "keycol2"],
- key_values=[["keyval1", "keyval2"], ["keyval3", "keyval4"]],
- value_names=["valuecol3"],
- value_values=[["val5"], ["val6"]],
- desc="",
- )
- )
- if USE_POSTGRES_FOR_TESTS:
- self.mock_execute_values.assert_called_once_with(
- self.mock_txn,
- "INSERT INTO tablename (keycol1, keycol2, valuecol3) VALUES ? ON CONFLICT (keycol1, keycol2) DO UPDATE SET valuecol3=EXCLUDED.valuecol3",
- [("keyval1", "keyval2", "val5"), ("keyval3", "keyval4", "val6")],
- template=None,
- fetch=False,
- )
- else:
- self.mock_txn.executemany.assert_called_once_with(
- "INSERT INTO tablename (keycol1, keycol2, valuecol3) VALUES (?, ?, ?) ON CONFLICT (keycol1, keycol2) DO UPDATE SET valuecol3=EXCLUDED.valuecol3",
- [("keyval1", "keyval2", "val5"), ("keyval3", "keyval4", "val6")],
- )
- @defer.inlineCallbacks
- def test_upsert_many_no_values(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert_many(
- table="tablename",
- key_names=["columnname"],
- key_values=[["oldvalue"]],
- value_names=[],
- value_values=[],
- desc="",
- )
- )
- if USE_POSTGRES_FOR_TESTS:
- self.mock_execute_values.assert_called_once_with(
- self.mock_txn,
- "INSERT INTO tablename (columnname) VALUES ? ON CONFLICT (columnname) DO NOTHING",
- [("oldvalue",)],
- template=None,
- fetch=False,
- )
- else:
- self.mock_txn.executemany.assert_called_once_with(
- "INSERT INTO tablename (columnname) VALUES (?) ON CONFLICT (columnname) DO NOTHING",
- [("oldvalue",)],
- )
- @defer.inlineCallbacks
- def test_upsert_emulated_no_values_exists(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.datastore.db_pool._unsafe_to_upsert_tables.add("tablename")
- self.mock_txn.fetchall.return_value = [(1,)]
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert(
- table="tablename",
- keyvalues={"columnname": "value"},
- values={},
- insertion_values={"columnname": "value"},
- )
- )
- if USE_POSTGRES_FOR_TESTS:
- self.mock_txn.execute.assert_has_calls(
- [
- call("LOCK TABLE tablename in EXCLUSIVE MODE", ()),
- call("SELECT 1 FROM tablename WHERE columnname = ?", ["value"]),
- ]
- )
- else:
- self.mock_txn.execute.assert_called_once_with(
- "SELECT 1 FROM tablename WHERE columnname = ?", ["value"]
- )
- self.assertFalse(result)
- @defer.inlineCallbacks
- def test_upsert_emulated_no_values_not_exists(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.datastore.db_pool._unsafe_to_upsert_tables.add("tablename")
- self.mock_txn.fetchall.return_value = []
- self.mock_txn.rowcount = 1
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert(
- table="tablename",
- keyvalues={"columnname": "value"},
- values={},
- insertion_values={"columnname": "value"},
- )
- )
- self.mock_txn.execute.assert_has_calls(
- [
- call(
- "SELECT 1 FROM tablename WHERE columnname = ?",
- ["value"],
- ),
- call("INSERT INTO tablename (columnname) VALUES (?)", ["value"]),
- ],
- )
- self.assertTrue(result)
- @defer.inlineCallbacks
- def test_upsert_emulated_with_insertion_exists(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.datastore.db_pool._unsafe_to_upsert_tables.add("tablename")
- self.mock_txn.rowcount = 1
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert(
- table="tablename",
- keyvalues={"columnname": "oldvalue"},
- values={"othercol": "newvalue"},
- insertion_values={"thirdcol": "insertionval"},
- )
- )
- if USE_POSTGRES_FOR_TESTS:
- self.mock_txn.execute.assert_has_calls(
- [
- call("LOCK TABLE tablename in EXCLUSIVE MODE", ()),
- call(
- "UPDATE tablename SET othercol = ? WHERE columnname = ?",
- ["newvalue", "oldvalue"],
- ),
- ]
- )
- else:
- self.mock_txn.execute.assert_called_once_with(
- "UPDATE tablename SET othercol = ? WHERE columnname = ?",
- ["newvalue", "oldvalue"],
- )
- self.assertTrue(result)
- @defer.inlineCallbacks
- def test_upsert_emulated_with_insertion_not_exists(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.datastore.db_pool._unsafe_to_upsert_tables.add("tablename")
- self.mock_txn.rowcount = 0
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert(
- table="tablename",
- keyvalues={"columnname": "oldvalue"},
- values={"othercol": "newvalue"},
- insertion_values={"thirdcol": "insertionval"},
- )
- )
- self.mock_txn.execute.assert_has_calls(
- [
- call(
- "UPDATE tablename SET othercol = ? WHERE columnname = ?",
- ["newvalue", "oldvalue"],
- ),
- call(
- "INSERT INTO tablename (columnname, othercol, thirdcol) VALUES (?, ?, ?)",
- ["oldvalue", "newvalue", "insertionval"],
- ),
- ]
- )
- self.assertTrue(result)
- @defer.inlineCallbacks
- def test_upsert_emulated_with_where(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.datastore.db_pool._unsafe_to_upsert_tables.add("tablename")
- self.mock_txn.rowcount = 1
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert(
- table="tablename",
- keyvalues={"columnname": "oldvalue"},
- values={"othercol": "newvalue"},
- where_clause="thirdcol IS NULL",
- )
- )
- if USE_POSTGRES_FOR_TESTS:
- self.mock_txn.execute.assert_has_calls(
- [
- call("LOCK TABLE tablename in EXCLUSIVE MODE", ()),
- call(
- "UPDATE tablename SET othercol = ? WHERE columnname = ? AND thirdcol IS NULL",
- ["newvalue", "oldvalue"],
- ),
- ]
- )
- else:
- self.mock_txn.execute.assert_called_once_with(
- "UPDATE tablename SET othercol = ? WHERE columnname = ? AND thirdcol IS NULL",
- ["newvalue", "oldvalue"],
- )
- self.assertTrue(result)
- @defer.inlineCallbacks
- def test_upsert_emulated_with_where_no_values(
- self,
- ) -> Generator["defer.Deferred[object]", object, None]:
- self.datastore.db_pool._unsafe_to_upsert_tables.add("tablename")
- self.mock_txn.rowcount = 1
- result = yield defer.ensureDeferred(
- self.datastore.db_pool.simple_upsert(
- table="tablename",
- keyvalues={"columnname": "oldvalue"},
- values={},
- where_clause="thirdcol IS NULL",
- )
- )
- if USE_POSTGRES_FOR_TESTS:
- self.mock_txn.execute.assert_has_calls(
- [
- call("LOCK TABLE tablename in EXCLUSIVE MODE", ()),
- call(
- "SELECT 1 FROM tablename WHERE columnname = ? AND thirdcol IS NULL",
- ["oldvalue"],
- ),
- ]
- )
- else:
- self.mock_txn.execute.assert_called_once_with(
- "SELECT 1 FROM tablename WHERE columnname = ? AND thirdcol IS NULL",
- ["oldvalue"],
- )
- self.assertFalse(result)
|