Lightweight 0.20260625.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 // Cast to unsigned char before std::isspace — see SqlConnectInfo.cpp for rationale.
28 while (!value.empty() && (std::isspace(static_cast<unsigned char>(value.back())) || value.back() == '\0'))
29 value.remove_suffix(1);
30 return value;
31 }
32
33 /// The @c sys.columns size/precision metrics that qualify a Microsoft SQL Server
34 /// column type. Grouped into one struct so the three same-typed integers cannot be
35 /// transposed at a call site.
36 struct MssqlColumnMetrics
37 {
38 /// @c sys.columns.max_length (bytes; @c -1 means MAX/LOB).
39 int maxLength = 0;
40 /// @c sys.columns.precision (for numeric/decimal/money types).
41 int precision = 0;
42 /// @c sys.columns.scale (for numeric/decimal/money types).
43 int scale = 0;
44 };
45
46 /// Maps a Microsoft SQL Server system type name (from @c sys.types.name) plus the
47 /// column's @c max_length / @c precision / @c scale (from @c sys.columns) to the
48 /// canonical @c SqlColumnTypeDefinition variant.
49 ///
50 /// This reproduces, for the batched MSSQL schema reader, exactly what the legacy
51 /// per-table path (@c MakeColumnTypeFromNative plus the MSSQL fixups in
52 /// @c SqlSchema.cpp) yields for the same column, so backup metadata stays
53 /// byte-identical.
54 ///
55 /// @param sysTypeName The system type name, e.g. @c "int", @c "nvarchar", @c "decimal".
56 /// @param metrics The column's @c max_length / @c precision / @c scale from @c sys.columns.
57 /// @return The mapped column type variant; defaults to @c Varchar for unknown names.
58 [[nodiscard]] LIGHTWEIGHT_API SqlColumnTypeDefinition MakeColumnTypeFromMssqlSysType(std::string_view sysTypeName,
59 MssqlColumnMetrics metrics);
60 } // namespace detail
61
62 struct FullyQualifiedTableName
63 {
64 std::string catalog;
65 std::string schema;
66 std::string table;
67
68 bool operator==(FullyQualifiedTableName const& other) const noexcept
69 {
70 return catalog == other.catalog && schema == other.schema && table == other.table;
71 }
72
73 bool operator!=(FullyQualifiedTableName const& other) const noexcept
74 {
75 return !(*this == other);
76 }
77
78 bool operator<(FullyQualifiedTableName const& other) const noexcept
79 {
80 return std::tie(catalog, schema, table) < std::tie(other.catalog, other.schema, other.table);
81 }
82 };
83
84 /// Identifies a single column within a schema (catalog.schema.table.column).
85 ///
86 /// Schema-introspection layer only — distinct from the query-builder DSL type
87 /// `Lightweight::SqlQualifiedTableColumnName`.
89 {
90 /// Fully qualified table the column belongs to.
91 FullyQualifiedTableName table;
92 /// Column name within the table.
93 std::string column;
94
95 /// Equality compares both the owning table and the column name.
96 bool operator==(ColumnIdentifier const& other) const noexcept
97 {
98 return table == other.table && column == other.column;
99 }
100
101 /// Inequality is the negation of equality.
102 bool operator!=(ColumnIdentifier const& other) const noexcept
103 {
104 return !(*this == other);
105 }
106
107 /// Lexicographic ordering by (table, column) so ColumnIdentifier can be used as a key in ordered containers.
108 bool operator<(ColumnIdentifier const& other) const noexcept
109 {
110 return std::tie(table, column) < std::tie(other.table, other.column);
111 }
112 };
113
114 /// Identifies an ordered set of columns within a single table (for composite keys / indexes).
116 {
117 /// Fully qualified table the column sequence belongs to.
118 FullyQualifiedTableName table;
119 /// Ordered list of column names (order is significant for composite keys / indexes).
120 std::vector<std::string> columns;
121 };
122
123 inline bool operator<(ColumnIdentifierSequence const& a, ColumnIdentifierSequence const& b) noexcept
124 {
125 return std::tie(a.table, a.columns) < std::tie(b.table, b.columns);
126 }
127
128 struct ForeignKeyConstraint
129 {
130 ColumnIdentifierSequence foreignKey;
131 ColumnIdentifierSequence primaryKey;
132 };
133
134 inline bool operator<(ForeignKeyConstraint const& a, ForeignKeyConstraint const& b) noexcept
135 {
136 return std::tie(a.foreignKey, a.primaryKey) < std::tie(b.foreignKey, b.primaryKey);
137 }
138
139 /// Represents an index definition on a table.
141 {
142 /// The name of the index.
143 std::string name;
144
145 /// The columns in the index (in order for composite indexes).
146 std::vector<std::string> columns;
147
148 /// Whether the index enforces uniqueness.
149 bool isUnique = false;
150 };
151
152 using KeyPair = std::pair<FullyQualifiedTableName /*fk table*/, FullyQualifiedTableName /*pk table*/>;
153
154 inline bool operator<(KeyPair const& a, KeyPair const& b)
155 {
156 return std::tie(a.first, a.second) < std::tie(b.first, b.second);
157 }
158
159 /// Holds the definition of a column in a SQL table as read from the database schema.
160 struct Column
161 {
162 /// The name of the column.
163 std::string name = {};
164 /// The SQL column type definition.
165 SqlColumnTypeDefinition type = {};
166 /// The dialect-dependent type string.
168 /// Whether the column allows NULL values.
169 bool isNullable = true;
170 /// Whether the column has a UNIQUE constraint.
171 bool isUnique = false;
172 /// The size of the column (for character/binary types).
173 size_t size = 0;
174 /// The number of decimal digits (for numeric types).
175 unsigned short decimalDigits = 0;
176 /// Whether the column auto-increments.
177 bool isAutoIncrement = false;
178 /// Whether the column is a primary key.
179 bool isPrimaryKey = false;
180 /// Whether the column is a foreign key.
181 bool isForeignKey = false;
182 /// The foreign key constraint, if any.
183 std::optional<ForeignKeyConstraint> foreignKeyConstraint {};
184 /// The default value of the column.
185 std::string defaultValue = {};
186 };
187
188 /// Callback interface for handling events while reading a database schema.
190 {
191 public:
192 /// Default constructor.
193 EventHandler() = default;
194 /// Default move constructor.
196 /// Default copy constructor.
197 EventHandler(EventHandler const&) = default;
198 /// Default move assignment operator.
200 /// Default copy assignment operator.
202 virtual ~EventHandler() = default;
203
204 /// Called when the names of all tables are read.
205 virtual void OnTables(std::vector<std::string> const& tables) = 0;
206
207 /// Called for each table. Returns true to process this table, false to skip it.
208 /// @param schema The schema the table belongs to.
209 /// @param table The name of the table.
210 virtual bool OnTable(std::string_view schema, std::string_view table) = 0;
211 /// Called when the primary keys of a table are read.
212 virtual void OnPrimaryKeys(std::string_view table, std::vector<std::string> const& columns) = 0;
213 /// Called when a foreign key constraint is read.
214 virtual void OnForeignKey(ForeignKeyConstraint const& foreignKeyConstraint) = 0;
215 /// Called for each column in a table.
216 virtual void OnColumn(Column const& column) = 0;
217 /// Called when an external foreign key referencing this table is read.
218 virtual void OnExternalForeignKey(ForeignKeyConstraint const& foreignKeyConstraint) = 0;
219 /// Called when the indexes of a table are read.
220 virtual void OnIndexes(std::vector<IndexDefinition> const& indexes) = 0;
221 /// Called when a table's schema reading is complete.
222 virtual void OnTableEnd() = 0;
223 };
224
225 /// Reads all tables in the given database and schema and calls the event handler for each table.
226 ///
227 /// @param stmt The SQL statement to use for reading the database schema.
228 /// @param database The name of the database to read the schema from.
229 /// @param schema The name of the schema to read the schema from.
230 /// @param eventHandler The SAX-style event handler to call for each table.
231 ///
232 /// @note The event handler is called for each table in the database and schema.
233 LIGHTWEIGHT_API void ReadAllTables(SqlStatement& stmt,
234 std::string_view database,
235 std::string_view schema,
236 EventHandler& eventHandler);
237
238 /// Holds the definition of a table in a SQL database as read from the database schema.
239 struct Table
240 {
241 // FullyQualifiedTableName name;
242
243 /// The schema the table belongs to.
244 std::string schema;
245
246 /// The name of the table.
247 std::string name;
248
249 /// The columns of the table.
250 std::vector<Column> columns {};
251
252 /// The foreign keys of the table.
253 std::vector<ForeignKeyConstraint> foreignKeys {};
254
255 /// The foreign keys of other tables that reference this table.
256 std::vector<ForeignKeyConstraint> externalForeignKeys {};
257
258 /// The primary keys of the table.
259 std::vector<std::string> primaryKeys {};
260
261 /// The indexes on the table (excluding primary key index).
262 std::vector<IndexDefinition> indexes {};
263 };
264
265 /// A list of tables.
266 using TableList = std::vector<Table>;
267
268 using ReadAllTablesCallback = std::function<void(std::string_view /*tableName*/, size_t /*current*/, size_t /*total*/)>;
269
270 /// Callback invoked when a table's schema is fully read.
271 ///
272 /// This callback is called for each table as soon as its schema (columns, keys, constraints)
273 /// is complete. Useful for streaming tables to consumers without waiting for all tables.
274 using TableReadyCallback = std::function<void(Table&&)>;
275
276 /// Predicate to filter tables before reading their full schema.
277 ///
278 /// @param schema The schema name.
279 /// @param tableName The table name.
280 /// @return true to include the table (read its full schema), false to skip it.
281 ///
282 /// When provided, tables that don't match the predicate will have their detailed
283 /// schema (columns, keys, constraints) skipped, improving performance when only
284 /// a subset of tables is needed.
285 using TableFilterPredicate = std::function<bool(std::string_view /*schema*/, std::string_view /*tableName*/)>;
286
287 /// Retrieves all tables in the given @p database and @p schema.
288 ///
289 /// @param stmt The SQL statement to use for reading.
290 /// @param database The database name.
291 /// @param schema The schema name (optional).
292 /// @param callback Progress callback invoked for each table during scanning.
293 /// @param tableReadyCallback Callback invoked when each table's schema is complete.
294 /// @param tableFilter Optional predicate to filter tables before reading their full schema.
295 /// If provided, only tables where the predicate returns true will have
296 /// their columns, keys, and constraints read.
297 LIGHTWEIGHT_API TableList ReadAllTables(SqlStatement& stmt,
298 std::string_view database,
299 std::string_view schema = {},
300 ReadAllTablesCallback callback = {},
301 TableReadyCallback tableReadyCallback = {},
302 TableFilterPredicate tableFilter = {});
303
304 /// Retrieves all tables in the given database and schema that have a foreign key to the given table.
305 LIGHTWEIGHT_API std::vector<ForeignKeyConstraint> AllForeignKeysTo(SqlStatement& stmt,
306 FullyQualifiedTableName const& table);
307
308 /// Retrieves all tables in the given database and schema that have a foreign key from the given table.
309 LIGHTWEIGHT_API std::vector<ForeignKeyConstraint> AllForeignKeysFrom(SqlStatement& stmt,
310 FullyQualifiedTableName const& table);
311
312 /// Creats an SQL CREATE TABLE plan for the given table description.
313 ///
314 /// @param tableDescription The description of the table to create the plan for.
315 ///
316 /// @return An SQL CREATE TABLE plan for the given table description.
317 LIGHTWEIGHT_API SqlCreateTablePlan MakeCreateTablePlan(Table const& tableDescription);
318
319 /// Creates an SQL CREATE TABLE plan for all the given table descriptions.
320 ///
321 /// @param tableDescriptions The descriptions of the tables to create the plan for.
322 ///
323 /// @return An SQL CREATE TABLE plan for all the given table descriptions.
324 LIGHTWEIGHT_API std::vector<SqlCreateTablePlan> MakeCreateTablePlan(TableList const& tableDescriptions);
325
326} // namespace SqlSchema
327
328} // namespace Lightweight
329
330template <>
331struct std::formatter<Lightweight::SqlSchema::FullyQualifiedTableName>: std::formatter<std::string>
332{
333 auto format(Lightweight::SqlSchema::FullyQualifiedTableName const& value, format_context& ctx) const
334 -> format_context::iterator
335 {
336 string output = std::string(Lightweight::SqlSchema::detail::rtrim(value.schema));
337 if (!output.empty())
338 output += '.';
339 auto const trimmedSchema = Lightweight::SqlSchema::detail::rtrim(value.catalog);
340 output += trimmedSchema;
341 if (!output.empty() && !trimmedSchema.empty())
342 output += '.';
343 output += Lightweight::SqlSchema::detail::rtrim(value.table);
344 return formatter<string>::format(output, ctx);
345 }
346};
347
348template <>
349struct std::formatter<Lightweight::SqlSchema::ColumnIdentifier>: std::formatter<std::string>
350{
351 auto format(Lightweight::SqlSchema::ColumnIdentifier const& value, format_context& ctx) const -> format_context::iterator
352 {
353 auto const table = std::format("{}", value.table);
354 if (table.empty())
355 return formatter<string>::format(std::format("{}", value.column), ctx);
356 else
357 return formatter<string>::format(std::format("{}.{}", value.table, value.column), ctx);
358 }
359};
360
361template <>
362struct std::formatter<Lightweight::SqlSchema::ColumnIdentifierSequence>: std::formatter<std::string>
363{
364 auto format(Lightweight::SqlSchema::ColumnIdentifierSequence const& value, format_context& ctx) const
365 -> format_context::iterator
366 {
367 auto const resolvedTableName = std::format("{}", value.table);
368 string output;
369 output += resolvedTableName;
370 output += '(';
371
372#if !defined(__cpp_lib_ranges_enumerate)
373 int i { -1 };
374 for (auto const& column: value.columns)
375 {
376 ++i;
377#else
378 for (auto const [i, column]: value.columns | std::views::enumerate)
379 {
380#endif
381 if (i != 0)
382 output += ", ";
383 output += column;
384 }
385 output += ')';
386
387 return formatter<string>::format(output, ctx);
388 }
389};
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.
Identifies an ordered set of columns within a single table (for composite keys / indexes).
std::vector< std::string > columns
Ordered list of column names (order is significant for composite keys / indexes).
FullyQualifiedTableName table
Fully qualified table the column sequence belongs to.
std::string column
Column name within the table.
Definition SqlSchema.hpp:93
bool operator==(ColumnIdentifier const &other) const noexcept
Equality compares both the owning table and the column name.
Definition SqlSchema.hpp:96
bool operator!=(ColumnIdentifier const &other) const noexcept
Inequality is the negation of equality.
bool operator<(ColumnIdentifier const &other) const noexcept
Lexicographic ordering by (table, column) so ColumnIdentifier can be used as a key in ordered contain...
FullyQualifiedTableName table
Fully qualified table the column belongs to.
Definition SqlSchema.hpp:91
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).