Lightweight 0.20260522.0
Loading...
Searching...
No Matches
SqlQueryFormatter.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "Api.hpp"
6#include "SqlConnection.hpp"
7#include "SqlQuery/MigrationPlan.hpp"
8#include "SqlServerType.hpp"
9
10#include <string>
11#include <string_view>
12
13namespace Lightweight
14{
15
16class SqlAdvisoryLockHandler;
17
18/// API to format SQL queries for different SQL dialects.
19class [[nodiscard]] LIGHTWEIGHT_API SqlQueryFormatter
20{
21 public:
22 /// Default constructor.
23 SqlQueryFormatter() = default;
24 /// Default move constructor.
26 /// Default copy constructor.
28 /// Default move assignment operator.
30 /// Default copy assignment operator.
32 virtual ~SqlQueryFormatter() = default;
33
34 /// Converts a boolean value to a string literal.
35 [[nodiscard]] virtual std::string_view BooleanLiteral(bool value) const noexcept = 0;
36
37 /// Returns the SQL function name used to retrieve the current date.
38 [[nodiscard]] virtual std::string_view DateFunction() const noexcept = 0;
39
40 /// Converts a string value to a string literal.
41 [[nodiscard]] virtual std::string StringLiteral(std::string_view value) const noexcept = 0;
42
43 /// Converts a character value to a string literal.
44 [[nodiscard]] virtual std::string StringLiteral(char value) const noexcept = 0;
45
46 /// Converts a binary value to a hex-encoded string literal.
47 [[nodiscard]] virtual std::string BinaryLiteral(std::span<uint8_t const> data) const = 0;
48
49 /// Formats a qualified table name with proper quoting for this database.
50 /// @param schema The schema name (can be empty for default schema)
51 /// @param table The table name
52 /// @return The properly quoted qualified table name (e.g., "schema"."table" or [schema].[table])
53 [[nodiscard]] virtual std::string QualifiedTableName(std::string_view schema, std::string_view table) const = 0;
54
55 /// Constructs an SQL INSERT query.
56 ///
57 /// @param intoTable The table to insert into.
58 /// @param fields The fields to insert into.
59 /// @param values The values to insert.
60 ///
61 /// The fields and values must be in the same order.
62 [[nodiscard]] virtual std::string Insert(std::string_view intoTable,
63 std::string_view fields,
64 std::string_view values) const = 0;
65
66 /// Constructs an SQL INSERT query with a schema prefix.
67 [[nodiscard]] virtual std::string Insert(std::string_view schema,
68 std::string_view intoTable,
69 std::string_view fields,
70 std::string_view values) const = 0;
71
72 /// Retrieves the last insert ID of the given table.
73 [[nodiscard]] virtual std::string QueryLastInsertId(std::string_view tableName) const = 0;
74
75 /// Constructs an SQL SELECT query for all rows.
76 [[nodiscard]] virtual std::string SelectAll(bool distinct,
77 std::string_view fields,
78 std::string_view fromTable,
79 std::string_view fromTableAlias,
80 std::string_view tableJoins,
81 std::string_view whereCondition,
82 std::string_view orderBy,
83 std::string_view groupBy) const = 0;
84
85 /// Constructs an SQL SELECT query for the first row.
86 [[nodiscard]] virtual std::string SelectFirst(bool distinct,
87 std::string_view fields,
88 std::string_view fromTable,
89 std::string_view fromTableAlias,
90 std::string_view tableJoins,
91 std::string_view whereCondition,
92 std::string_view orderBy,
93 size_t count) const = 0;
94
95 /// Constructs an SQL SELECT query for a range of rows.
96 [[nodiscard]] virtual std::string SelectRange(bool distinct,
97 std::string_view fields,
98 std::string_view fromTable,
99 std::string_view fromTableAlias,
100 std::string_view tableJoins,
101 std::string_view whereCondition,
102 std::string_view orderBy,
103 std::string_view groupBy,
104 std::size_t offset,
105 std::size_t limit) const = 0;
106
107 /// Constructs an SQL SELECT query retrieve the count of rows matching the given condition.
108 [[nodiscard]] virtual std::string SelectCount(bool distinct,
109 std::string_view fromTable,
110 std::string_view fromTableAlias,
111 std::string_view tableJoins,
112 std::string_view whereCondition) const = 0;
113
114 /// Constructs an SQL UPDATE query.
115 [[nodiscard]] virtual std::string Update(std::string_view table,
116 std::string_view tableAlias,
117 std::string_view setFields,
118 std::string_view whereCondition) const = 0;
119
120 /// Constructs an SQL DELETE query.
121 [[nodiscard]] virtual std::string Delete(std::string_view fromTable,
122 std::string_view fromTableAlias,
123 std::string_view tableJoins,
124 std::string_view whereCondition) const = 0;
125
126 /// Alias for a list of SQL statement strings.
127 using StringList = std::vector<std::string>;
128
129 /// Convert the given column type definition to the SQL type.
130 [[nodiscard]] virtual std::string ColumnType(SqlColumnTypeDefinition const& type) const = 0;
131
132 /// Constructs an SQL CREATE TABLE query.
133 ///
134 /// @param schema The schema name of the table to create.
135 /// @param tableName The name of the table to create.
136 /// @param columns The columns of the table.
137 /// @param foreignKeys The foreign key constraints of the table.
138 /// @param ifNotExists If true, generates CREATE TABLE IF NOT EXISTS instead of CREATE TABLE.
139 [[nodiscard]] virtual StringList CreateTable(std::string_view schema,
140 std::string_view tableName,
141 std::vector<SqlColumnDeclaration> const& columns,
142 std::vector<SqlCompositeForeignKeyConstraint> const& foreignKeys,
143 bool ifNotExists = false) const = 0;
144
145 /// Constructs an SQL ALTER TABLE query.
146 [[nodiscard]] virtual StringList AlterTable(std::string_view schema,
147 std::string_view tableName,
148 std::vector<SqlAlterTableCommand> const& commands) const = 0;
149
150 /// Constructs an SQL DROP TABLE query.
151 ///
152 /// @param schema The schema name of the table to drop.
153 /// @param tableName The name of the table to drop.
154 /// @param ifExists If true, generates DROP TABLE IF EXISTS instead of DROP TABLE.
155 /// @param cascade If true, drops all foreign key constraints referencing this table first.
156 [[nodiscard]] virtual StringList DropTable(std::string_view schema,
157 std::string_view const& tableName,
158 bool ifExists = false,
159 bool cascade = false) const = 0;
160
161 /// Returns the SQL query to retrieve the full server version string.
162 ///
163 /// This query returns detailed version information specific to each database:
164 /// - SQL Server: Returns result of SELECT @@VERSION (includes build, edition, OS info)
165 /// - PostgreSQL: Returns result of SELECT version() (includes build info)
166 /// - SQLite: Returns result of SELECT sqlite_version()
167 [[nodiscard]] virtual std::string QueryServerVersion() const = 0;
168
169 /// Retrieves the SQL query formatter for SQLite.
170 static SqlQueryFormatter const& Sqlite();
171
172 /// Retrieves the SQL query formatter for Microsoft SQL server.
173 static SqlQueryFormatter const& SqlServer();
174
175 /// Retrieves the SQL query formatter for PostgreSQL.
176 static SqlQueryFormatter const& PostgrSQL();
177
178 /// Retrieves the SQL query formatter for the given SqlServerType.
179 static SqlQueryFormatter const* Get(SqlServerType serverType) noexcept;
180
181 /// @brief Whether the dialect must rebuild a table to add or drop a foreign-key
182 /// constraint (i.e. cannot express it via `ALTER TABLE … ADD/DROP CONSTRAINT`).
183 ///
184 /// SQLite returns `true`; every other backend defaults to `false`. The migration
185 /// executor consults this to decide whether to take the table-rebuild path on
186 /// `AlterTable` steps that touch foreign keys.
187 [[nodiscard]] virtual bool RequiresTableRebuildForForeignKeyChange() const noexcept
188 {
189 return false;
190 }
191
192 /// @brief Builds the canonical foreign-key constraint name for a set of columns.
193 ///
194 /// Produces `FK_<table>_<col1>[_<col2>…]`. A single-column FK collapses to
195 /// `FK_<table>_<col>`; a composite FK includes every column so that a composite
196 /// FK whose first column matches an existing single-column FK doesn't collide
197 /// on the constraint name (which MSSQL enforces as globally unique per DB).
198 ///
199 /// Consumers include the SQL Server, PostgreSQL and SQLite formatters —
200 /// centralising the convention here keeps CREATE/ALTER emission in sync with
201 /// DROP CONSTRAINT lookup (e.g. `SQLiteRebuildDropForeignKey`).
202 template <typename Range>
203 [[nodiscard]] static std::string BuildForeignKeyConstraintName(std::string_view tableName, Range const& columns)
204 {
205 std::string name { "FK_" };
206 name.append(tableName);
207 for (auto const& col: columns)
208 {
209 name.push_back('_');
210 name.append(col);
211 }
212 return name;
213 }
214
215 /// @brief Returns the SQL statement to execute after connect to make
216 /// `schema` the connection-level default for unqualified DDL/DML.
217 ///
218 /// Returns an empty string when the DBMS has no session-level concept of a
219 /// default schema (SQL Server, SQLite). Callers must skip emission for
220 /// empty results. PostgreSQL implements this via `SET search_path TO ...`.
221 ///
222 /// The `schema` value is interpolated into the statement; callers must
223 /// validate it (e.g. whitelist `[A-Za-z0-9_]`) before invoking.
224 [[nodiscard]] virtual std::string SetDefaultSchemaStatement(std::string_view schema) const
225 {
226 (void) schema;
227 return {};
228 }
229
230 /// @brief Returns the dialect-specific handler used by `SqlScopedLock` to
231 /// acquire and release named cross-process advisory locks.
232 ///
233 /// The returned reference is to a process-singleton, valid for the lifetime
234 /// of the program. Each backend implements its own primitive — SQL Server
235 /// uses `sp_getapplock`, PostgreSQL uses `pg_advisory_lock`, SQLite uses
236 /// a lock table — and the implementation lives in the backend's formatter
237 /// translation unit, so adding a new dialect only touches that unit and
238 /// `SqlScopedLock` itself stays dialect-agnostic.
239 [[nodiscard]] virtual SqlAdvisoryLockHandler const& AdvisoryLockOps() const = 0;
240
241 protected:
242 /// Formats a table name with optional schema prefix.
243 static std::string FormatTableName(std::string_view schema, std::string_view table);
244};
245
246} // namespace Lightweight
API to format SQL queries for different SQL dialects.
virtual std::string SetDefaultSchemaStatement(std::string_view schema) const
Returns the SQL statement to execute after connect to make schema the connection-level default for un...
SqlQueryFormatter(SqlQueryFormatter const &)=default
Default copy constructor.
SqlQueryFormatter()=default
Default constructor.
SqlQueryFormatter & operator=(SqlQueryFormatter &&)=default
Default move assignment operator.
virtual SqlAdvisoryLockHandler const & AdvisoryLockOps() const =0
Returns the dialect-specific handler used by SqlScopedLock to acquire and release named cross-process...
static std::string BuildForeignKeyConstraintName(std::string_view tableName, Range const &columns)
Builds the canonical foreign-key constraint name for a set of columns.
virtual std::string_view DateFunction() const noexcept=0
Returns the SQL function name used to retrieve the current date.
std::vector< std::string > StringList
Alias for a list of SQL statement strings.
SqlQueryFormatter(SqlQueryFormatter &&)=default
Default move constructor.
SqlQueryFormatter & operator=(SqlQueryFormatter const &)=default
Default copy assignment operator.
virtual std::string_view BooleanLiteral(bool value) const noexcept=0
Converts a boolean value to a string literal.
static std::string FormatTableName(std::string_view schema, std::string_view table)
Formats a table name with optional schema prefix.
std::variant< SqlAlterTableCommands::RenameTable, SqlAlterTableCommands::AddColumn, SqlAlterTableCommands::AddColumnIfNotExists, SqlAlterTableCommands::AlterColumn, SqlAlterTableCommands::AddIndex, SqlAlterTableCommands::RenameColumn, SqlAlterTableCommands::DropColumn, SqlAlterTableCommands::DropColumnIfExists, SqlAlterTableCommands::DropIndex, SqlAlterTableCommands::DropIndexIfExists, SqlAlterTableCommands::AddForeignKey, SqlAlterTableCommands::AddCompositeForeignKey, SqlAlterTableCommands::DropForeignKey > SqlAlterTableCommand
Represents a single SQL ALTER TABLE command.
Represents a SQL column declaration.
Represents a composite foreign key constraint.