Lightweight 0.20251202.0
Loading...
Searching...
No Matches
SqlSchema.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "Api.hpp"
6#include "SqlQuery/MigrationPlan.hpp"
7
8#include <format>
9#include <functional>
10#include <string_view>
11#include <tuple>
12#include <vector>
13
14namespace Lightweight
15{
16
17class SqlStatement;
18
19namespace SqlSchema
20{
21
22 namespace detail
23 {
24 // NOLINTNEXTLINE(readability-identifier-naming)
25 constexpr std::string_view rtrim(std::string_view value) noexcept
26 {
27 while (!value.empty() && (std::isspace(value.back()) || value.back() == '\0'))
28 value.remove_suffix(1);
29 return value;
30 }
31 } // namespace detail
32
33 struct FullyQualifiedTableName
34 {
35 std::string catalog;
36 std::string schema;
37 std::string table;
38
39 bool operator==(FullyQualifiedTableName const& other) const noexcept
40 {
41 return catalog == other.catalog && schema == other.schema && table == other.table;
42 }
43
44 bool operator!=(FullyQualifiedTableName const& other) const noexcept
45 {
46 return !(*this == other);
47 }
48
49 bool operator<(FullyQualifiedTableName const& other) const noexcept
50 {
51 return std::tie(catalog, schema, table) < std::tie(other.catalog, other.schema, other.table);
52 }
53 };
54
55 struct FullyQualifiedTableColumn
56 {
57 FullyQualifiedTableName table;
58 std::string column;
59
60 bool operator==(FullyQualifiedTableColumn const& other) const noexcept
61 {
62 return table == other.table && column == other.column;
63 }
64
65 bool operator!=(FullyQualifiedTableColumn const& other) const noexcept
66 {
67 return !(*this == other);
68 }
69
70 bool operator<(FullyQualifiedTableColumn const& other) const noexcept
71 {
72 return std::tie(table, column) < std::tie(other.table, other.column);
73 }
74 };
75
76 struct FullyQualifiedTableColumnSequence
77 {
78 FullyQualifiedTableName table;
79 std::vector<std::string> columns;
80 };
81
82 inline bool operator<(FullyQualifiedTableColumnSequence const& a, FullyQualifiedTableColumnSequence const& b) noexcept
83 {
84 return std::tie(a.table, a.columns) < std::tie(b.table, b.columns);
85 }
86
87 struct ForeignKeyConstraint
88 {
89 FullyQualifiedTableColumnSequence foreignKey;
90 FullyQualifiedTableColumnSequence primaryKey;
91 };
92
93 inline bool operator<(ForeignKeyConstraint const& a, ForeignKeyConstraint const& b) noexcept
94 {
95 return std::tie(a.foreignKey, a.primaryKey) < std::tie(b.foreignKey, b.primaryKey);
96 }
97
98 /// Represents an index definition on a table.
100 {
101 /// The name of the index.
102 std::string name;
103
104 /// The columns in the index (in order for composite indexes).
105 std::vector<std::string> columns;
106
107 /// Whether the index enforces uniqueness.
108 bool isUnique = false;
109 };
110
111 using KeyPair = std::pair<FullyQualifiedTableName /*fk table*/, FullyQualifiedTableName /*pk table*/>;
112
113 inline bool operator<(KeyPair const& a, KeyPair const& b)
114 {
115 return std::tie(a.first, a.second) < std::tie(b.first, b.second);
116 }
117
118 /// Holds the definition of a column in a SQL table as read from the database schema.
119 struct Column
120 {
121 std::string name = {};
122 SqlColumnTypeDefinition type = {};
123 std::string dialectDependantTypeString = {};
124 bool isNullable = true;
125 bool isUnique = false;
126 size_t size = 0;
127 unsigned short decimalDigits = 0;
128 bool isAutoIncrement = false;
129 bool isPrimaryKey = false;
130 bool isForeignKey = false;
131 std::optional<ForeignKeyConstraint> foreignKeyConstraint {};
132 std::string defaultValue = {};
133 };
134
135 /// Callback interface for handling events while reading a database schema.
137 {
138 public:
139 EventHandler() = default;
140 EventHandler(EventHandler&&) = default;
141 EventHandler(EventHandler const&) = default;
142 EventHandler& operator=(EventHandler&&) = default;
143 EventHandler& operator=(EventHandler const&) = default;
144 virtual ~EventHandler() = default;
145
146 /// Called when the names of all tables are read.
147 virtual void OnTables(std::vector<std::string> const& tables) = 0;
148
149 /// Called for each table. Returns true to process this table, false to skip it.
150 /// @param schema The schema the table belongs to.
151 /// @param table The name of the table.
152 virtual bool OnTable(std::string_view schema, std::string_view table) = 0;
153 virtual void OnPrimaryKeys(std::string_view table, std::vector<std::string> const& columns) = 0;
154 virtual void OnForeignKey(ForeignKeyConstraint const& foreignKeyConstraint) = 0;
155 virtual void OnColumn(Column const& column) = 0;
156 virtual void OnExternalForeignKey(ForeignKeyConstraint const& foreignKeyConstraint) = 0;
157 virtual void OnIndexes(std::vector<IndexDefinition> const& indexes) = 0;
158 virtual void OnTableEnd() = 0;
159 };
160
161 /// Reads all tables in the given database and schema and calls the event handler for each table.
162 ///
163 /// @param stmt The SQL statement to use for reading the database schema.
164 /// @param database The name of the database to read the schema from.
165 /// @param schema The name of the schema to read the schema from.
166 /// @param eventHandler The SAX-style event handler to call for each table.
167 ///
168 /// @note The event handler is called for each table in the database and schema.
169 LIGHTWEIGHT_API void ReadAllTables(SqlStatement& stmt,
170 std::string_view database,
171 std::string_view schema,
172 EventHandler& eventHandler);
173
174 /// Holds the definition of a table in a SQL database as read from the database schema.
175 struct Table
176 {
177 // FullyQualifiedTableName name;
178
179 /// The schema the table belongs to.
180 std::string schema;
181
182 /// The name of the table.
183 std::string name;
184
185 /// The columns of the table.
186 std::vector<Column> columns {};
187
188 /// The foreign keys of the table.
189 std::vector<ForeignKeyConstraint> foreignKeys {};
190
191 /// The foreign keys of other tables that reference this table.
192 std::vector<ForeignKeyConstraint> externalForeignKeys {};
193
194 /// The primary keys of the table.
195 std::vector<std::string> primaryKeys {};
196
197 /// The indexes on the table (excluding primary key index).
198 std::vector<IndexDefinition> indexes {};
199 };
200
201 /// A list of tables.
202 using TableList = std::vector<Table>;
203
204 using ReadAllTablesCallback = std::function<void(std::string_view /*tableName*/, size_t /*current*/, size_t /*total*/)>;
205
206 /// Callback invoked when a table's schema is fully read.
207 ///
208 /// This callback is called for each table as soon as its schema (columns, keys, constraints)
209 /// is complete. Useful for streaming tables to consumers without waiting for all tables.
210 using TableReadyCallback = std::function<void(Table&&)>;
211
212 /// Predicate to filter tables before reading their full schema.
213 ///
214 /// @param schema The schema name.
215 /// @param tableName The table name.
216 /// @return true to include the table (read its full schema), false to skip it.
217 ///
218 /// When provided, tables that don't match the predicate will have their detailed
219 /// schema (columns, keys, constraints) skipped, improving performance when only
220 /// a subset of tables is needed.
221 using TableFilterPredicate = std::function<bool(std::string_view /*schema*/, std::string_view /*tableName*/)>;
222
223 /// Retrieves all tables in the given @p database and @p schema.
224 ///
225 /// @param stmt The SQL statement to use for reading.
226 /// @param database The database name.
227 /// @param schema The schema name (optional).
228 /// @param callback Progress callback invoked for each table during scanning.
229 /// @param tableReadyCallback Callback invoked when each table's schema is complete.
230 /// @param tableFilter Optional predicate to filter tables before reading their full schema.
231 /// If provided, only tables where the predicate returns true will have
232 /// their columns, keys, and constraints read.
233 LIGHTWEIGHT_API TableList ReadAllTables(SqlStatement& stmt,
234 std::string_view database,
235 std::string_view schema = {},
236 ReadAllTablesCallback callback = {},
237 TableReadyCallback tableReadyCallback = {},
238 TableFilterPredicate tableFilter = {});
239
240 /// Retrieves all tables in the given database and schema that have a foreign key to the given table.
241 LIGHTWEIGHT_API std::vector<ForeignKeyConstraint> AllForeignKeysTo(SqlStatement& stmt,
242 FullyQualifiedTableName const& table);
243
244 /// Retrieves all tables in the given database and schema that have a foreign key from the given table.
245 LIGHTWEIGHT_API std::vector<ForeignKeyConstraint> AllForeignKeysFrom(SqlStatement& stmt,
246 FullyQualifiedTableName const& table);
247
248 /// Creats an SQL CREATE TABLE plan for the given table description.
249 ///
250 /// @param tableDescription The description of the table to create the plan for.
251 ///
252 /// @return An SQL CREATE TABLE plan for the given table description.
253 LIGHTWEIGHT_API SqlCreateTablePlan MakeCreateTablePlan(Table const& tableDescription);
254
255 /// Creates an SQL CREATE TABLE plan for all the given table descriptions.
256 ///
257 /// @param tableDescriptions The descriptions of the tables to create the plan for.
258 ///
259 /// @return An SQL CREATE TABLE plan for all the given table descriptions.
260 LIGHTWEIGHT_API std::vector<SqlCreateTablePlan> MakeCreateTablePlan(TableList const& tableDescriptions);
261
262} // namespace SqlSchema
263
264} // namespace Lightweight
265
266template <>
267struct std::formatter<Lightweight::SqlSchema::FullyQualifiedTableName>: std::formatter<std::string>
268{
269 auto format(Lightweight::SqlSchema::FullyQualifiedTableName const& value, format_context& ctx) const
270 -> format_context::iterator
271 {
272 string output = std::string(Lightweight::SqlSchema::detail::rtrim(value.schema));
273 if (!output.empty())
274 output += '.';
275 auto const trimmedSchema = Lightweight::SqlSchema::detail::rtrim(value.catalog);
276 output += trimmedSchema;
277 if (!output.empty() && !trimmedSchema.empty())
278 output += '.';
279 output += Lightweight::SqlSchema::detail::rtrim(value.table);
280 return formatter<string>::format(output, ctx);
281 }
282};
283
284template <>
285struct std::formatter<Lightweight::SqlSchema::FullyQualifiedTableColumn>: std::formatter<std::string>
286{
287 auto format(Lightweight::SqlSchema::FullyQualifiedTableColumn const& value, format_context& ctx) const
288 -> format_context::iterator
289 {
290 auto const table = std::format("{}", value.table);
291 if (table.empty())
292 return formatter<string>::format(std::format("{}", value.column), ctx);
293 else
294 return formatter<string>::format(std::format("{}.{}", value.table, value.column), ctx);
295 }
296};
297
298template <>
299struct std::formatter<Lightweight::SqlSchema::FullyQualifiedTableColumnSequence>: std::formatter<std::string>
300{
301 auto format(Lightweight::SqlSchema::FullyQualifiedTableColumnSequence const& value, format_context& ctx) const
302 -> format_context::iterator
303 {
304 auto const resolvedTableName = std::format("{}", value.table);
305 string output;
306 output += resolvedTableName;
307 output += '(';
308
309#if !defined(__cpp_lib_ranges_enumerate)
310 int i { -1 };
311 for (auto const& column: value.columns)
312 {
313 ++i;
314#else
315 for (auto const [i, column]: value.columns | std::views::enumerate)
316 {
317#endif
318 if (i != 0)
319 output += ", ";
320 output += column;
321 }
322 output += ')';
323
324 return formatter<string>::format(output, ctx);
325 }
326};
Callback interface for handling events while reading a database schema.
virtual void OnTables(std::vector< std::string > const &tables)=0
Called when the names of all tables are read.
virtual bool OnTable(std::string_view schema, std::string_view table)=0
High level API for (prepared) raw SQL statements.
Holds the definition of a column in a SQL table as read from the database schema.
Represents an index definition on a table.
std::string name
The name of the index.
std::vector< std::string > columns
The columns in the index (in order for composite indexes).
bool isUnique
Whether the index enforces uniqueness.
Holds the definition of a table in a SQL database as read from the database schema.
std::vector< ForeignKeyConstraint > foreignKeys
The foreign keys of the table.
std::vector< std::string > primaryKeys
The primary keys of the table.
std::string schema
The schema the table belongs to.
std::vector< ForeignKeyConstraint > externalForeignKeys
The foreign keys of other tables that reference this table.
std::string name
The name of the table.
std::vector< Column > columns
The columns of the table.
std::vector< IndexDefinition > indexes
The indexes on the table (excluding primary key index).