Skip to content

Commit 66f8376

Browse files
authored
[DEV-2354] Added SqlTable as a base class for SqliteTable (#195)
Signed-off-by: Max Chesterfield <max.chesterfield@zepben.com>
1 parent 79bb5ef commit 66f8376

124 files changed

Lines changed: 291 additions & 240 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

changelog.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@
8787
* `CURRENT_NETWORK_STATE` -> `CURRENT`.
8888
* The `zepben.auth` dependency has been incorporated into the SDK with the following package change:
8989
* `zepben.auth` -> `zepben.ewb.auth`. You can also import these directly from `zepben.ewb`.
90+
* `SqliteTable` now subclasses `SqlTable`.
9091

9192
### New Features
92-
* None.
93+
* Created a new `SqlTable` that doesn't support creating schema creation statements by default.
9394

9495
### Enhancements
9596
* `EquipmentTreeBuilder` will now calculate `leaves` when specified to do so.

src/zepben/ewb/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@
353353
from zepben.ewb.database.paths.database_type import *
354354
from zepben.ewb.database.paths.ewb_data_file_paths import *
355355

356-
from zepben.ewb.database.sqlite.tables.column import *
356+
from zepben.ewb.database.sql.column import *
357357
from zepben.ewb.database.sqlite.tables.sqlite_table import *
358358
from zepben.ewb.database.sqlite.tables.table_metadata_data_sources import *
359359
from zepben.ewb.database.sqlite.tables.table_version import *
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright 2025 Zeppelin Bend Pty Ltd
2+
# This Source Code Form is subject to the terms of the Mozilla Public
3+
# License, v. 2.0. If a copy of the MPL was not distributed with this
4+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

src/zepben/ewb/database/sqlite/tables/column.py renamed to src/zepben/ewb/database/sql/column.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 Zeppelin Bend Pty Ltd
1+
# Copyright 2025 Zeppelin Bend Pty Ltd
22
# This Source Code Form is subject to the terms of the Mozilla Public
33
# License, v. 2.0. If a copy of the MPL was not distributed with this
44
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Copyright 2025 Zeppelin Bend Pty Ltd
2+
# This Source Code Form is subject to the terms of the Mozilla Public
3+
# License, v. 2.0. If a copy of the MPL was not distributed with this
4+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
from __future__ import annotations
6+
7+
__all__ = ["SqlTable"]
8+
9+
from abc import abstractmethod, ABCMeta
10+
from operator import attrgetter
11+
from typing import List, Optional, Type, Any, Generator
12+
13+
from zepben.ewb.database.sql.column import Column, Nullable
14+
15+
16+
class SqlTable(metaclass=ABCMeta):
17+
"""
18+
Represents a table in an SQL Database.
19+
20+
By default, this class doesn't support creating schema creation statements, allowing support for database with external schema management.
21+
"""
22+
23+
column_index: int = 0
24+
"""Used to specify index of the column in the table during initialisation. Always increment BEFORE creating a Column. Indices start from 1."""
25+
26+
_column_set: Optional[List[Column]] = None
27+
_create_table_sql: Optional[str] = None
28+
_prepared_insert_sql: Optional[str] = None
29+
_prepared_update_sql: Optional[str] = None
30+
_create_indexes_sql: Optional[List[str]] = None
31+
_select_sql: Optional[str] = None
32+
33+
@property
34+
@abstractmethod
35+
def name(self) -> str:
36+
"""
37+
The name of the table in the actual database.
38+
"""
39+
pass
40+
41+
#
42+
# NOTE: This function is called `description` in teh JVM SDK, but in the python SDK this
43+
# conflicts with the `description` column of `TableIdentifiedObjects`.
44+
#
45+
def describe(self) -> str:
46+
"""
47+
Readable description of the contents of the table for adding to logs.
48+
"""
49+
return self.name.replace("_", " ")
50+
51+
@property
52+
@abstractmethod
53+
def create_table_sql(self):
54+
"""
55+
The SQL statement that should be executed to create the table in the database.
56+
"""
57+
raise NotImplemented
58+
59+
@property
60+
def prepared_insert_sql(self):
61+
"""
62+
The SQL statement that should be used with a `PreparedStatement` to insert entries into the table.
63+
"""
64+
if self._prepared_insert_sql is None:
65+
self._prepared_insert_sql = (f"INSERT INTO {self.name} ({', '.join([c.name for c in self.column_set])}) "
66+
f"VALUES ({', '.join(['?' for _ in self.column_set])})")
67+
return self._prepared_insert_sql
68+
69+
@property
70+
@abstractmethod
71+
def create_indexes_sql(self):
72+
"""
73+
The SQL statement that should be executed to create the indexes for the table in the database. Should be executed after all
74+
entries are inserted into the table.
75+
"""
76+
raise NotImplemented
77+
78+
@property
79+
def select_sql(self):
80+
"""
81+
The SQL statement that should be used to read the entries from the table in the database.
82+
"""
83+
if self._select_sql is None:
84+
self._select_sql = f"SELECT {', '.join([c.name for c in self.column_set])} FROM {self.name}"
85+
return self._select_sql
86+
87+
@property
88+
def prepared_update_sql(self):
89+
"""
90+
The SQL statement that should be used with a `PreparedStatement` to update entries into the table.
91+
"""
92+
if self._prepared_update_sql is None:
93+
self._prepared_update_sql = f"UPDATE {self.name} SET {', '.join([f'{c.name} = ?' for c in self.column_set])}"
94+
return self._prepared_update_sql
95+
96+
@property
97+
def unique_index_columns(self) -> Generator[List[Column], None, None]:
98+
"""
99+
A list of column groups that require a unique index in the database
100+
"""
101+
yield from []
102+
103+
@property
104+
def non_unique_index_columns(self) -> Generator[List[Column], None, None]:
105+
"""
106+
A list of column groups that require a non-unique index in the database
107+
"""
108+
yield from []
109+
110+
@property
111+
def column_set(self) -> List[Column]:
112+
if self._column_set is None:
113+
self._column_set = list(self._build_column_set(self.__class__, self))
114+
return self._column_set
115+
116+
@staticmethod
117+
def _build_column_set(clazz: Type[Any], instance: SqlTable) -> Generator[Column, None, None]:
118+
"""
119+
Builds the list of columns for use in DDL statements for this table.
120+
121+
:param clazz: The class of this table.
122+
:param instance:
123+
"""
124+
cols = list()
125+
for field, x in instance.__dict__.items():
126+
if isinstance(x, Column):
127+
if x.query_index != len(cols) + 1:
128+
raise ValueError(
129+
f"Field {field} in SQL Table class {clazz.__name__} is using an invalid column index. "
130+
f"Did you forget to increment column_index, or did you skip one?"
131+
)
132+
cols.append(x)
133+
134+
if len(set([c.name for c in cols])) != len(cols):
135+
raise ValueError("You have duplicate column names, go fix that.")
136+
137+
yield from sorted(cols, key=attrgetter('query_index'))
138+
139+
def _create_column(self, name: str, type_: str, nullable: Nullable = Nullable.NONE) -> Column:
140+
self.column_index += 1
141+
# noinspection PyArgumentList
142+
return Column(self.column_index, name, type_, nullable)

src/zepben/ewb/database/sqlite/tables/associations/table_asset_organisation_roles_assets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from typing import List, Generator
99

10-
from zepben.ewb.database.sqlite.tables.column import Column, Nullable
10+
from zepben.ewb.database.sql.column import Column, Nullable
1111
from zepben.ewb.database.sqlite.tables.sqlite_table import SqliteTable
1212

1313

src/zepben/ewb/database/sqlite/tables/associations/table_assets_power_system_resources.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from typing import List, Generator
99

10-
from zepben.ewb.database.sqlite.tables.column import Column, Nullable
10+
from zepben.ewb.database.sql.column import Column, Nullable
1111
from zepben.ewb.database.sqlite.tables.sqlite_table import SqliteTable
1212

1313

src/zepben/ewb/database/sqlite/tables/associations/table_battery_units_battery_controls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from typing import List, Generator
99

10-
from zepben.ewb.database.sqlite.tables.column import Column, Nullable
10+
from zepben.ewb.database.sql.column import Column, Nullable
1111
from zepben.ewb.database.sqlite.tables.sqlite_table import SqliteTable
1212

1313

src/zepben/ewb/database/sqlite/tables/associations/table_circuits_substations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from typing import List, Generator
99

10-
from zepben.ewb.database.sqlite.tables.column import Column, Nullable
10+
from zepben.ewb.database.sql.column import Column, Nullable
1111
from zepben.ewb.database.sqlite.tables.sqlite_table import SqliteTable
1212

1313

src/zepben/ewb/database/sqlite/tables/associations/table_circuits_terminals.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from typing import List, Generator
99

10-
from zepben.ewb.database.sqlite.tables.column import Column, Nullable
10+
from zepben.ewb.database.sql.column import Column, Nullable
1111
from zepben.ewb.database.sqlite.tables.sqlite_table import SqliteTable
1212

1313

0 commit comments

Comments
 (0)