Repository: rotki/rotki

Issue:
Filtering by customized events does not work in the non premium version
## Problem Definition

Trying to filter by customized events in 1.31.2 history events does not work if the user is not premium. Throws a 500 error.


https://github.com/rotki/rotki/assets/1658405/c7975164-6e20-468a-a944-99b854cd6ba3



## Logs

```
[29/01/2024 00:11:34 CET] DEBUG rotkehlchen.api.server Greenlet-3: start rotki api OPTIONS /api/1/history/events view_args={}, query_string=b''
[29/01/2024 00:11:34 CET] DEBUG rotkehlchen.api.server Greenlet-3: end rotki api OPTIONS /api/1/history/events view_args={}, query_string=b'', status_code=200, result=None
[29/01/2024 00:11:34 CET] DEBUG rotkehlchen.api.server Greenlet-3: start rotki api POST /api/1/history/events view_args={}, query_string=b''
[29/01/2024 00:11:34 CET] CRITICAL rotkehlchen.api.server Greenlet-3: Unhandled exception when processing endpoint request exc_info=True, exception=no such column: history_events.identifier
[29/01/2024 00:11:34 CET] DEBUG rotkehlchen.api.server Greenlet-3: end rotki api POST /api/1/history/events view_args={}, query_string=b'', status_code=500, result={'result': None, 'message': 'no such column: history_events.identifier'}
[29/01/2024 00:11:36 CET] DEBUG rotkehlchen.api.server Greenlet-3: start rotki api GET /api/1/periodic view_args={}, query_string=b''
```

## Task

Solve it.


Discussion:

Resolving patch:
diff --git a/rotkehlchen/db/filtering.py b/rotkehlchen/db/filtering.py
--- a/rotkehlchen/db/filtering.py
+++ b/rotkehlchen/db/filtering.py
@@ -2,7 +2,7 @@
 from abc import ABCMeta, abstractmethod
 from collections.abc import Collection
 from dataclasses import dataclass, field
-from typing import Any, Generic, Literal, NamedTuple, TypeVar, cast
+from typing import Any, Generic, Literal, NamedTuple, TypeVar
 
 from rotkehlchen.accounting.structures.base import HistoryBaseEntryType
 from rotkehlchen.accounting.structures.evm_event import EvmProduct
@@ -32,6 +32,11 @@
 )
 from rotkehlchen.utils.misc import ts_now
 
+
+class InvalidFilter(Exception):
+    """Raised if an invalid filter combination has been given"""
+
+
 logger = logging.getLogger(__name__)
 log = RotkehlchenLogsAdapter(logger)
 
@@ -418,11 +423,10 @@ class EvmTransactionsFilterQuery(DBFilterQuery, FilterWithTimestamp):
 
     @property
     def accounts(self) -> list[EvmAccount] | None:
-        if self.join_clause is None:
+        if not isinstance(self.join_clause, DBEvmTransactionJoinsFilter):
             return None
 
-        ethaddress_filter = cast('DBEvmTransactionJoinsFilter', self.join_clause)
-        return ethaddress_filter.accounts
+        return self.join_clause.accounts
 
     @property
     def chain_id(self) -> SUPPORTED_CHAIN_IDS | None:
@@ -443,6 +447,9 @@ def make(
             tx_hash: EVMTxHash | None = None,
             chain_id: SUPPORTED_CHAIN_IDS | None = None,
     ) -> 'EvmTransactionsFilterQuery':
+        """May raise:
+        - InvalidFilter for invalid combination of filters
+        """
         if order_by_rules is None:
             order_by_rules = [('timestamp', True)]
 
@@ -466,6 +473,9 @@ def make(
 
         else:
             if accounts is not None:
+                if filter_query.join_clause is not None:  # atm "should not happen"
+                    raise InvalidFilter('Unable to filter by account due to conflicting filter')
+
                 filter_query.join_clause = DBEvmTransactionJoinsFilter(
                     and_op=False,
                     accounts=accounts,
@@ -856,7 +866,7 @@ class HistoryEventCustomizedOnlyJoinsFilter(DBFilter):
     def prepare(self) -> tuple[list[str], list[Any]]:
         query = (
             'INNER JOIN history_events_mappings '
-            'ON history_events_mappings.parent_identifier = history_events.identifier '
+            'ON history_events_mappings.parent_identifier = history_events_identifier '
             'WHERE history_events_mappings.name = ? AND history_events_mappings.value = ?'
         )
         bindings: list[str | int] = [HISTORY_MAPPING_KEY_STATE, HISTORY_MAPPING_STATE_CUSTOMIZED]
@@ -887,6 +897,9 @@ def make(
             exclude_ignored_assets: bool = False,
             customized_events_only: bool = False,
     ) -> T_HistoryBaseEntryFilterQ:
+        """May raise:
+        - InvalidFilter for invalid combination of filters
+        """
         if order_by_rules is None:
             order_by_rules = [('timestamp', True), ('sequence_index', True)]
 
@@ -898,6 +911,9 @@ def make(
             group_by_field='event_identifier',
         )
         if customized_events_only is True:
+            if filter_query.join_clause is not None:  # atm "should not happen"
+                raise InvalidFilter('Unable to filter by customized events due to conflicting filter')  # noqa: E501
+
             filter_query.join_clause = HistoryEventCustomizedOnlyJoinsFilter(and_op=True)
 
         filters: list[DBFilter] = []
diff --git a/rotkehlchen/db/history_events.py b/rotkehlchen/db/history_events.py
--- a/rotkehlchen/db/history_events.py
+++ b/rotkehlchen/db/history_events.py
@@ -54,7 +54,8 @@
 log = RotkehlchenLogsAdapter(logger)
 
 
-HISTORY_BASE_ENTRY_FIELDS = 'entry_type, history_events.identifier, event_identifier, sequence_index, timestamp, location, location_label, asset, amount, usd_value, notes, type, subtype '  # noqa: E501
+# Giving a name for history_events.identifier since without it in the free version case https://github.com/rotki/rotki/issues/7362 we were hitting a no such column: history_events.identifier  # noqa: E501
+HISTORY_BASE_ENTRY_FIELDS = 'entry_type, history_events.identifier AS history_events_identifier, event_identifier, sequence_index, timestamp, location, location_label, asset, amount, usd_value, notes, type, subtype '  # noqa: E501
 HISTORY_BASE_ENTRY_LENGTH = 12
 
 EVM_EVENT_FIELDS = 'tx_hash, counterparty, product, address, extra_data'


Test patch:
diff --git a/rotkehlchen/tests/db/test_history_events.py b/rotkehlchen/tests/db/test_history_events.py
--- a/rotkehlchen/tests/db/test_history_events.py
+++ b/rotkehlchen/tests/db/test_history_events.py
@@ -1,5 +1,7 @@
 from typing import Any
 
+import pytest
+
 from rotkehlchen.accounting.structures.balance import Balance
 from rotkehlchen.accounting.structures.base import HistoryBaseEntryType, HistoryEvent
 from rotkehlchen.accounting.structures.eth2 import EthDepositEvent, EthWithdrawalEvent
@@ -217,7 +219,10 @@ def test_read_write_events_from_db(database):
                 assert event == expected_event
 
 
-def test_read_write_customized_events_from_db(database: DBHandler) -> None:
+@pytest.mark.parametrize(
+    ('has_premium', 'group_by_event_ids'), [(True, False), (True, True), (False, False), (False, True)],  # noqa: E501
+)
+def test_read_write_customized_events_from_db(database: DBHandler, has_premium: bool, group_by_event_ids: bool) -> None:  # noqa: E501
     """Tests filtering for fetching only the customized events"""
     db = DBHistoryEvents(database)
     history_data = {
@@ -261,14 +266,15 @@ def test_read_write_customized_events_from_db(database: DBHandler) -> None:
 
     with db.db.conn.read_ctx() as cursor:
         for (filtering_class, filter_args), expected_ids in zip(filter_query_args, expected_identifiers, strict=True):  # noqa: E501
-            events = db.get_history_events(
+            events = db.get_history_events(  # type: ignore
                 cursor=cursor,
                 filter_query=filtering_class.make(**filter_args),
-                has_premium=True,
-                group_by_event_ids=False,
+                has_premium=has_premium,
+                group_by_event_ids=group_by_event_ids,
             )
-            filtered_ids = [x.tx_hash if isinstance(x, EvmEvent) else x.event_identifier for x in events]  # noqa: E501
-            assert filtered_ids == expected_ids
+            if group_by_event_ids is False:  # don't check the grouping case. Just make sure no exception is raised  # noqa: E501
+                filtered_ids = [x.tx_hash if isinstance(x, EvmEvent) else x.event_identifier for x in events]  # noqa: E501
+                assert filtered_ids == expected_ids
 
 
 def test_delete_last_event(database):