Compare commits

...

12 Commits

Author SHA1 Message Date
Andrew Morgan
2edc839c23 Switch to f-strings 2022-08-08 14:11:34 +01:00
Andrew Morgan
90128dd355 Raise a ValueError if keyvalues is falsely 2022-08-08 12:13:17 +01:00
Andrew Morgan
efc492ed99 Do not return the number of deleted rows from simple_truncate
Postgres does not support returning this information for a TRUNCATE command.
2022-08-04 16:37:10 +01:00
Andrew Morgan
1da8f115b5 update the changelog entries 2022-08-04 15:27:06 +01:00
Andrew Morgan
452fc33646 Prevent empty keyvalues arg to simple_delete raising db exception 2022-08-04 15:26:59 +01:00
Andrew Morgan
1777a21d12 Add a test for simple_truncate 2022-08-04 15:21:29 +01:00
Andrew Morgan
da7c7f39c4 Add simple_truncate database method. 2022-08-04 14:54:55 +01:00
Andrew Morgan
8346060c06 Remove unnecessary if condition
'clauses' will always be non-None.
2022-08-04 14:15:37 +01:00
Andrew Morgan
ae08e4cd43 Revert "Allow deleting all rows when passing empty keyvalues dict to simple_delete{_many}"
This reverts commit 7496a37e03.
2022-08-04 14:14:38 +01:00
Andrew Morgan
2f255be9b2 changelogs 2022-08-03 15:43:11 +01:00
Andrew Morgan
7496a37e03 Allow deleting all rows when passing empty keyvalues dict to simple_delete{_many} 2022-08-03 14:39:43 +01:00
Andrew Morgan
fcf058f564 Remove unused 'DataStore.get_users' method 2022-08-03 14:39:37 +01:00
5 changed files with 115 additions and 24 deletions

1
changelog.d/13446.misc Normal file
View File

@@ -0,0 +1 @@
Allow dropping all rows from a database table via the `simple_truncate` storage method.

View File

@@ -0,0 +1 @@
Remove the unused `get_users` storage method.

View File

@@ -2108,15 +2108,26 @@ class DatabasePool:
) -> int:
"""Executes a DELETE query on the named table.
Filters rows by the key-value pairs.
Filter rows by the key-value pairs.
Args:
table: string giving the table name
keyvalues: dict of column names and values to select the row with
keyvalues: dict of column names and values to select the row with. Must
not be empty.
Returns:
The number of deleted rows.
Raises:
ValueError: if keyvalues was a falsey value, such as an empty dict.
"""
if not keyvalues:
raise ValueError(
"'keyvalues' arg to simple_delete_txn was falsey. If you were trying to "
"delete all rows from a database, perhaps try "
"DatabasePool.simple_truncate instead?"
)
sql = "DELETE FROM %s WHERE %s" % (
table,
" AND ".join("%s = ?" % (k,) for k in keyvalues),
@@ -2195,12 +2206,49 @@ class DatabasePool:
clauses.append("%s = ?" % (key,))
values.append(value)
if clauses:
sql = "%s WHERE %s" % (sql, " AND ".join(clauses))
sql = "%s WHERE %s" % (sql, " AND ".join(clauses))
txn.execute(sql, values)
return txn.rowcount
async def simple_truncate(self, table: str, desc: str) -> None:
"""Executes a TRUNCATE query on the given table, deleting all rows.
SQLite does not support TRUNCATE, thus a 'DELETE FROM table_name' will
be used instead. This method does not return the number of rows deleted,
as this is not returned by postgres for TRUNCATE commands.
Args:
table: The name of the table to delete all rows from.
desc: description of the transaction, for logging and metrics.
"""
await self.runInteraction(
desc, self._simple_truncate_txn, table, db_autocommit=True
)
@staticmethod
def _simple_truncate_txn(
txn: LoggingTransaction,
table: str,
) -> None:
"""Executes a TRUNCATE query on the given table, deleting all rows.
SQLite does not support TRUNCATE, thus a 'DELETE FROM table_name' will
be used instead. This method does not return the number of rows deleted,
as this is not returned by postgres for TRUNCATE commands.
Args:
txn: Transaction object
table: The name of the table to delete all rows from.
"""
if isinstance(txn.database_engine, PostgresEngine):
sql = f"TRUNCATE {table}"
else:
# SQLite does not support the TRUNCATE command
sql = f"DELETE FROM {table}"
txn.execute(sql)
def get_cache_dict(
self,
db_conn: LoggingDatabaseConnection,

View File

@@ -173,26 +173,6 @@ class DataStore(
# TODO: shouldn't this be moved to `DeviceWorkerStore`?
return self._device_list_id_gen.get_current_token()
async def get_users(self) -> List[JsonDict]:
"""Function to retrieve a list of users in users table.
Returns:
A list of dictionaries representing users.
"""
return await self.db_pool.simple_select_list(
table="users",
keyvalues={},
retcols=[
"name",
"password_hash",
"is_guest",
"admin",
"user_type",
"deactivated",
],
desc="get_users",
)
async def get_users_paginate(
self,
start: int,

View File

@@ -1,5 +1,6 @@
# Copyright 2015, 2016 OpenMarket Ltd
# Copyright 2019 New Vector Ltd
# Copyright 2022 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -152,3 +153,63 @@ class UpdateUpsertManyTests(unittest.HomeserverTestCase):
set(self._dump_table_to_tuple()),
{(1, "alice", "aaa!"), (2, "bob", "bbb!"), (3, "charlie", "C")},
)
class SimpleTruncateTestCase(unittest.HomeserverTestCase):
"""Tests for the DatabasePool.simple_truncate storage method."""
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
"""Create and populate a database table to run tests against."""
self.storage = hs.get_datastores().main
self.table_name = "test_database_table"
self.get_success(
self.storage.db_pool.runInteraction(
"simple_truncate_test_create",
lambda x, *a: x.execute(*a),
"CREATE TABLE %s (id INTEGER, value TEXT)" % self.table_name,
)
)
# And add some rows to the table.
self.get_success(
self.storage.db_pool.simple_insert_many(
table=self.table_name,
keys=("id", "value"),
values=[(1, "A"), (2, "B"), (3, "C")],
desc="simple_truncate_test_insert",
)
)
def test_simple_truncate_deletes_all_rows(self) -> None:
"""Test that simple_truncate deletes all rows from a database table."""
table_rows = self.get_success(
self.storage.db_pool.simple_select_list(
table=self.table_name,
keyvalues=None,
retcols=("id",),
desc="simple_truncate_test_select",
)
)
# Ensure the table has some rows for us to test deleting.
self.assertGreater(len(table_rows), 0)
# Attempt to truncate the table
self.get_success(
self.storage.db_pool.simple_truncate(
table=self.table_name,
desc="simple_truncate_test_truncate",
)
)
# Perform another select and ensure there are no remaining rows.
table_rows = self.get_success(
self.storage.db_pool.simple_select_list(
table=self.table_name,
keyvalues=None,
retcols=("id",),
desc="simple_truncate_test_select",
)
)
self.assertEqual(len(table_rows), 0)