Lightweight 0.20260303.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 /// The name of the column.
122 std::string name = {};
123 /// The SQL column type definition.
124 SqlColumnTypeDefinition type = {};
125 /// The dialect-dependent type string.
127 /// Whether the column allows NULL values.
128 bool isNullable = true;
129 /// Whether the column has a UNIQUE constraint.
130 bool isUnique = false;
131 /// The size of the column (for character/binary types).
132 size_t size = 0;
133 /// The number of decimal digits (for numeric types).
134 unsigned short decimalDigits = 0;
135 /// Whether the column auto-increments.
136 bool isAutoIncrement = false;
137 /// Whether the column is a primary key.
138 bool isPrimaryKey = false;
139 /// Whether the column is a foreign key.
140 bool isForeignKey = false;
141 /// The foreign key constraint, if any.
142 std::optional<ForeignKeyConstraint> foreignKeyConstraint {};
143 /// The default value of the column.
144 std::string defaultValue = {};
145 };
146
147 /// Callback interface for handling events while reading a database schema.
149 {
150 public:
151 /// Default constructor.
152 EventHandler() = default;
153 /// Default move constructor.
155 /// Default copy constructor.
156 EventHandler(EventHandler const&) = default;
157 /// Default move assignment operator.
159 /// Default copy assignment operator.
161 virtual ~EventHandler() = default;
162
163 /// Called when the names of all tables are read.
164 virtual void OnTables(std::vector<std::string> const& tables) = 0;
165
166 /// Called for each table. Returns true to process this table, false to skip it.
167 /// @param schema The schema the table belongs to.
168 /// @param table The name of the table.
169 virtual bool OnTable(std::string_view schema, std::string_view table) = 0;
170 /// Called when the primary keys of a table are read.
171 virtual void OnPrimaryKeys(std::string_view table, std::vector<std::string> const& columns) = 0;
172 /// Called when a foreign key constraint is read.
173 virtual void OnForeignKey(ForeignKeyConstraint const& foreignKeyConstraint) = 0;
174 /// Called for each column in a table.
175 virtual void OnColumn(Column const& column) = 0;
176 /// Called when an external foreign key referencing this table is read.
177 virtual void OnExternalForeignKey(ForeignKeyConstraint const& foreignKeyConstraint) = 0;
178 /// Called when the indexes of a table are read.
179 virtual void OnIndexes(std::vector<IndexDefinition> const& indexes) = 0;
180 /// Called when a table's schema reading is complete.
181 virtual void OnTableEnd() = 0;
182 };
183
184 /// Reads all tables in the given database and schema and calls the event handler for each table.
185 ///
186 /// @param stmt The SQL statement to use for reading the database schema.
187 /// @param database The name of the database to read the schema from.
188 /// @param schema The name of the schema to read the schema from.
189 /// @param eventHandler The SAX-style event handler to call for each table.
190 ///
191 /// @note The event handler is called for each table in the database and schema.
192 LIGHTWEIGHT_API void ReadAllTables(SqlStatement& stmt,
193 std::string_view database,
194 std::string_view schema,
195 EventHandler& eventHandler);
196
197 /// Holds the definition of a table in a SQL database as read from the database schema.
198 struct Table
199 {
200 // FullyQualifiedTableName name;
201
202 /// The schema the table belongs to.
203 std::string schema;
204
205 /// The name of the table.
206 std::string name;
207
208 /// The columns of the table.
209 std::vector<Column> columns {};
210
211 /// The foreign keys of the table.
212 std::vector<ForeignKeyConstraint> foreignKeys {};
213
214 /// The foreign keys of other tables that reference this table.
215 std::vector<ForeignKeyConstraint> externalForeignKeys {};
216
217 /// The primary keys of the table.
218 std::vector<std::string> primaryKeys {};
219
220 /// The indexes on the table (excluding primary key index).
221 std::vector<IndexDefinition> indexes {};
222 };
223
224 /// A list of tables.
225 using TableList = std::vector<Table>;
226
227 using ReadAllTablesCallback = std::function<void(std::string_view /*tableName*/, size_t /*current*/, size_t /*total*/)>;
228
229 /// Callback invoked when a table's schema is fully read.
230 ///
231 /// This callback is called for each table as soon as its schema (columns, keys, constraints)
232 /// is complete. Useful for streaming tables to consumers without waiting for all tables.
233 using TableReadyCallback = std::function<void(Table&&)>;
234
235 /// Predicate to filter tables before reading their full schema.
236 ///
237 /// @param schema The schema name.
238 /// @param tableName The table name.
239 /// @return true to include the table (read its full schema), false to skip it.
240 ///
241 /// When provided, tables that don't match the predicate will have their detailed
242 /// schema (columns, keys, constraints) skipped, improving performance when only
243 /// a subset of tables is needed.
244 using TableFilterPredicate = std::function<bool(std::string_view /*schema*/, std::string_view /*tableName*/)>;
245
246 /// Retrieves all tables in the given @p database and @p schema.
247 ///
248 /// @param stmt The SQL statement to use for reading.
249 /// @param database The database name.
250 /// @param schema The schema name (optional).
251 /// @param callback Progress callback invoked for each table during scanning.
252 /// @param tableReadyCallback Callback invoked when each table's schema is complete.
253 /// @param tableFilter Optional predicate to filter tables before reading their full schema.
254 /// If provided, only tables where the predicate returns true will have
255 /// their columns, keys, and constraints read.
256 LIGHTWEIGHT_API TableList ReadAllTables(SqlStatement& stmt,
257 std::string_view database,
258 std::string_view schema = {},
259 ReadAllTablesCallback callback = {},
260 TableReadyCallback tableReadyCallback = {},
261 TableFilterPredicate tableFilter = {});
262
263 /// Retrieves all tables in the given database and schema that have a foreign key to the given table.
264 LIGHTWEIGHT_API std::vector<ForeignKeyConstraint> AllForeignKeysTo(SqlStatement& stmt,
265 FullyQualifiedTableName const& table);
266
267 /// Retrieves all tables in the given database and schema that have a foreign key from the given table.
268 LIGHTWEIGHT_API std::vector<ForeignKeyConstraint> AllForeignKeysFrom(SqlStatement& stmt,
269 FullyQualifiedTableName const& table);
270
271 /// Creats an SQL CREATE TABLE plan for the given table description.
272 ///
273 /// @param tableDescription The description of the table to create the plan for.
274 ///
275 /// @return An SQL CREATE TABLE plan for the given table description.
276 LIGHTWEIGHT_API SqlCreateTablePlan MakeCreateTablePlan(Table const& tableDescription);
277
278 /// Creates an SQL CREATE TABLE plan for all the given table descriptions.
279 ///
280 /// @param tableDescriptions The descriptions of the tables to create the plan for.
281 ///
282 /// @return An SQL CREATE TABLE plan for all the given table descriptions.
283 LIGHTWEIGHT_API std::vector<SqlCreateTablePlan> MakeCreateTablePlan(TableList const& tableDescriptions);
284
285} // namespace SqlSchema
286
287} // namespace Lightweight
288
289template <>
290struct std::formatter<Lightweight::SqlSchema::FullyQualifiedTableName>: std::formatter<std::string>
291{
292 auto format(Lightweight::SqlSchema::FullyQualifiedTableName const& value, format_context& ctx) const
293 -> format_context::iterator
294 {
295 string output = std::string(Lightweight::SqlSchema::detail::rtrim(value.schema));
296 if (!output.empty())
297 output += '.';
298 auto const trimmedSchema = Lightweight::SqlSchema::detail::rtrim(value.catalog);
299 output += trimmedSchema;
300 if (!output.empty() && !trimmedSchema.empty())
301 output += '.';
302 output += Lightweight::SqlSchema::detail::rtrim(value.table);
303 return formatter<string>::format(output, ctx);
304 }
305};
306
307template <>
308struct std::formatter<Lightweight::SqlSchema::FullyQualifiedTableColumn>: std::formatter<std::string>
309{
310 auto format(Lightweight::SqlSchema::FullyQualifiedTableColumn const& value, format_context& ctx) const
311 -> format_context::iterator
312 {
313 auto const table = std::format("{}", value.table);
314 if (table.empty())
315 return formatter<string>::format(std::format("{}", value.column), ctx);
316 else
317 return formatter<string>::format(std::format("{}.{}", value.table, value.column), ctx);
318 }
319};
320
321template <>
322struct std::formatter<Lightweight::SqlSchema::FullyQualifiedTableColumnSequence>: std::formatter<std::string>
323{
324 auto format(Lightweight::SqlSchema::FullyQualifiedTableColumnSequence const& value, format_context& ctx) const
325 -> format_context::iterator
326 {
327 auto const resolvedTableName = std::format("{}", value.table);
328 string output;
329 output += resolvedTableName;
330 output += '(';
331
332#if !defined(__cpp_lib_ranges_enumerate)
333 int i { -1 };
334 for (auto const& column: value.columns)
335 {
336 ++i;
337#else
338 for (auto const [i, column]: value.columns | std::views::enumerate)
339 {
340#endif
341 if (i != 0)
342 output += ", ";
343 output += column;
344 }
345 output += ')';
346
347 return formatter<string>::format(output, ctx);
348 }
349};
Callback interface for handling events while reading a database schema.
virtual void OnColumn(Column const &column)=0
Called for each column in a table.
EventHandler(EventHandler &&)=default
Default move constructor.
virtual void OnIndexes(std::vector< IndexDefinition > const &indexes)=0
Called when the indexes of a table are read.
virtual void OnPrimaryKeys(std::string_view table, std::vector< std::string > const &columns)=0
Called when the primary keys of a table are read.
virtual void OnTables(std::vector< std::string > const &tables)=0
Called when the names of all tables are read.
EventHandler & operator=(EventHandler &&)=default
Default move assignment operator.
EventHandler & operator=(EventHandler const &)=default
Default copy assignment operator.
virtual void OnTableEnd()=0
Called when a table's schema reading is complete.
EventHandler()=default
Default constructor.
virtual bool OnTable(std::string_view schema, std::string_view table)=0
virtual void OnExternalForeignKey(ForeignKeyConstraint const &foreignKeyConstraint)=0
Called when an external foreign key referencing this table is read.
virtual void OnForeignKey(ForeignKeyConstraint const &foreignKeyConstraint)=0
Called when a foreign key constraint is read.
EventHandler(EventHandler const &)=default
Default copy constructor.
High level API for (prepared) raw SQL statements.
Holds the definition of a column in a SQL table as read from the database schema.
std::string name
The name of the column.
bool isNullable
Whether the column allows NULL values.
std::string defaultValue
The default value of the column.
bool isForeignKey
Whether the column is a foreign key.
SqlColumnTypeDefinition type
The SQL column type definition.
bool isPrimaryKey
Whether the column is a primary key.
std::string dialectDependantTypeString
The dialect-dependent type string.
std::optional< ForeignKeyConstraint > foreignKeyConstraint
The foreign key constraint, if any.
size_t size
The size of the column (for character/binary types).
bool isUnique
Whether the column has a UNIQUE constraint.
unsigned short decimalDigits
The number of decimal digits (for numeric types).
bool isAutoIncrement
Whether the column auto-increments.
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).