Skip to content

Commit 0d7d0ba

Browse files
WIP
Signed-off-by: Christian Parpart <christian@parpart.family>
1 parent 8481480 commit 0d7d0ba

File tree

11 files changed

+334
-14
lines changed

11 files changed

+334
-14
lines changed

.vimspector.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
11
{
22
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#",
33
"configurations": {
4+
"dbcopy from and to SQLite3": {
5+
"adapter": "vscode-cpptools",
6+
"configuration": {
7+
"request": "launch",
8+
"program": "${workspaceRoot}/out/build/linux-clang-debug/src/tools/dbcopy",
9+
"args": [
10+
"--from\\=DRIVER\\=SQLite3\\;DATABASE\\=${workspaceRoot}/Chinook.sqlite",
11+
"--to\\=DRIVER\\=SQLite3\\;DATABASE\\=${workspaceRoot}/output.sqlite"
12+
],
13+
"cwd": "${workspaceRoot}",
14+
"externalConsole": true,
15+
"stopAtEntry": false,
16+
"MIMode": "gdb"
17+
},
18+
"breakpoints": {
19+
"exception": {
20+
"caught": "Y",
21+
"uncaught": "Y"
22+
}
23+
}
24+
},
425
"CoreTest - SQLite": {
526
"adapter": "vscode-cpptools",
627
"configuration": {

src/Lightweight/SqlConnection.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ SqlConnection::SqlConnection(std::optional<SqlConnectionString> connectInfo):
4343
SQLAllocHandle(SQL_HANDLE_DBC, m_hEnv, &m_hDbc);
4444

4545
if (connectInfo.has_value())
46-
Connect(std::move(*connectInfo));
46+
{
47+
auto const success = Connect(std::move(*connectInfo));
48+
if (!success)
49+
;
50+
}
4751
}
4852

4953
SqlConnection::SqlConnection(SqlConnection&& other) noexcept:
@@ -189,7 +193,12 @@ bool SqlConnection::Connect(SqlConnectionString sqlConnectionString) noexcept
189193
nullptr,
190194
SQL_DRIVER_NOPROMPT);
191195
if (!SQL_SUCCEEDED(sqlResult))
196+
{
197+
auto const errorInfo = SqlErrorInfo::fromConnectionHandle(m_hDbc);
198+
SqlLogger::GetLogger().OnError(errorInfo);
199+
// throw SqlException(std::move(errorInfo));
192200
return false;
201+
}
193202

194203
sqlResult = SQLSetConnectAttrA(m_hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER);
195204
if (!SQL_SUCCEEDED(sqlResult))

src/Lightweight/SqlError.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ struct SqlErrorInfo
4343
return fromHandle(SQL_HANDLE_STMT, hStmt);
4444
}
4545

46+
static SqlErrorInfo fromEnvironmentHandle(SQLHSTMT hEnv)
47+
{
48+
return fromHandle(SQL_HANDLE_ENV, hEnv);
49+
}
50+
4651
/// Asserts that the given result is a success code, otherwise throws an exception.
4752
static void RequireStatementSuccess(SQLRETURN result, SQLHSTMT hStmt, std::string_view message);
4853

src/Lightweight/SqlQuery/MigrationPlan.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ std::vector<std::string> SqlMigrationPlan::ToSql() const
1313
}
1414
return result;
1515
}
16-
1716
std::vector<std::string> ToSql(SqlQueryFormatter const& formatter, SqlMigrationPlanElement const& element)
1817
{
1918
using namespace std::string_literals;
@@ -38,3 +37,20 @@ std::vector<std::string> ToSql(SqlQueryFormatter const& formatter, SqlMigrationP
3837
},
3938
element);
4039
}
40+
41+
std::vector<std::string> ToSql(std::vector<SqlMigrationPlan> const& plans)
42+
{
43+
std::vector<std::string> result;
44+
45+
for (auto const& plan: plans)
46+
{
47+
for (auto const& step: plan.steps)
48+
{
49+
auto subSteps = ToSql(plan.formatter, step);
50+
result.insert(result.end(), subSteps.begin(), subSteps.end());
51+
}
52+
}
53+
54+
return result;
55+
}
56+

src/Lightweight/SqlQuery/MigrationPlan.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,3 +357,5 @@ struct [[nodiscard]] SqlMigrationPlan
357357

358358
[[nodiscard]] LIGHTWEIGHT_API std::vector<std::string> ToSql() const;
359359
};
360+
361+
[[nodiscard]] std::vector<std::string> ToSql(SqlQueryFormatter const& formatter, std::vector<SqlMigrationPlan> const& elements);

src/Lightweight/SqlSchema.cpp

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,12 @@ namespace
4444
return { std::move(first), std::move(second) };
4545
}
4646

47-
std::vector<std::string> AllTables(std::string_view database, std::string_view schema)
47+
std::vector<std::string> AllTables(SqlStatement& stmt, std::string_view database, std::string_view schema)
4848
{
4949
auto const tableType = "TABLE"sv;
5050
(void) database;
5151
(void) schema;
5252

53-
auto stmt = SqlStatement();
5453
auto sqlResult = SQLTables(stmt.NativeHandle(),
5554
(SQLCHAR*) database.data(),
5655
(SQLSMALLINT) database.size(),
@@ -173,10 +172,9 @@ namespace
173172
} // namespace
174173

175174
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
176-
void ReadAllTables(std::string_view database, std::string_view schema, EventHandler& eventHandler)
175+
void ReadAllTables(SqlStatement& stmt, std::string_view database, std::string_view schema, EventHandler& eventHandler)
177176
{
178-
auto stmt = SqlStatement {};
179-
auto const tableNames = AllTables(database, schema);
177+
auto const tableNames = AllTables(stmt, database, schema);
180178

181179
eventHandler.OnTables(tableNames);
182180

@@ -206,7 +204,7 @@ void ReadAllTables(std::string_view database, std::string_view schema, EventHand
206204
for (auto const& foreignKey: incomingForeignKeys)
207205
eventHandler.OnExternalForeignKey(foreignKey);
208206

209-
auto columnStmt = SqlStatement();
207+
auto columnStmt = SqlStatement { stmt.Connection() };
210208
auto const sqlResult = SQLColumns(columnStmt.NativeHandle(),
211209
(SQLCHAR*) database.data(),
212210
(SQLSMALLINT) database.size(),
@@ -274,7 +272,6 @@ void ReadAllTables(std::string_view database, std::string_view schema, EventHand
274272

275273
// accumulated properties
276274
column.isPrimaryKey = std::ranges::contains(primaryKeys, column.name);
277-
// column.isForeignKey = ...;
278275
column.isForeignKey = std::ranges::any_of(foreignKeys, [&column](ForeignKeyConstraint const& fk) {
279276
return std::ranges::contains(fk.foreignKey.columns, column.name);
280277
});
@@ -301,7 +298,10 @@ std::string ToLowerCase(std::string_view str)
301298
return result;
302299
}
303300

304-
TableList ReadAllTables(std::string_view database, std::string_view schema, ReadAllTablesCallback callback)
301+
TableList ReadAllTables(SqlStatement& stmt,
302+
std::string_view database,
303+
std::string_view schema,
304+
ReadAllTablesCallback callback)
305305
{
306306
TableList tables;
307307
struct EventHandler: public SqlSchema::EventHandler
@@ -353,7 +353,7 @@ TableList ReadAllTables(std::string_view database, std::string_view schema, Read
353353
tables.back().externalForeignKeys.emplace_back(foreignKeyConstraint);
354354
}
355355
} eventHandler { tables, std::move(callback) };
356-
ReadAllTables(database, schema, eventHandler);
356+
ReadAllTables(stmt, database, schema, eventHandler);
357357

358358
std::map<std::string, std::string> tableNameCaseMap;
359359
for (auto const& table: tables)
@@ -388,4 +388,43 @@ std::vector<ForeignKeyConstraint> AllForeignKeysFrom(SqlStatement& stmt, FullyQu
388388
return AllForeignKeys(stmt, FullyQualifiedTableName {}, table);
389389
}
390390

391+
SqlCreateTablePlan MakeCreateTablePlan(Table const& tableDescription)
392+
{
393+
auto plan = SqlCreateTablePlan {}; // TODO(pr)
394+
395+
plan.tableName = tableDescription.name;
396+
397+
for (auto const& columnDescription: tableDescription.columns)
398+
{
399+
auto columnDecl = SqlColumnDeclaration {
400+
.name = columnDescription.name,
401+
.type = columnDescription.type,
402+
.primaryKey = [&] {
403+
if (columnDescription.isAutoIncrement)
404+
return SqlPrimaryKeyType::AUTO_INCREMENT;
405+
406+
if (columnDescription.isPrimaryKey)
407+
return SqlPrimaryKeyType::MANUAL;
408+
409+
return SqlPrimaryKeyType::NONE;
410+
}(),
411+
};
412+
413+
plan.columns.emplace_back(std::move(columnDecl));
414+
}
415+
// TODO(pr) foreign keys
416+
417+
return plan;
418+
}
419+
420+
std::vector<SqlCreateTablePlan> MakeCreateTablePlan(TableList const& tableDescriptions)
421+
{
422+
auto result = std::vector<SqlCreateTablePlan>();
423+
424+
for (auto const& tableDescription: tableDescriptions)
425+
result.emplace_back(MakeCreateTablePlan(tableDescription));
426+
427+
return result;
428+
}
429+
391430
} // namespace SqlSchema

src/Lightweight/SqlSchema.hpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,10 @@ class EventHandler
130130
};
131131

132132
/// Reads all tables in the given database and schema and calls the event handler for each table.
133-
LIGHTWEIGHT_API void ReadAllTables(std::string_view database, std::string_view schema, EventHandler& eventHandler);
133+
LIGHTWEIGHT_API void ReadAllTables(SqlStatement& stmt,
134+
std::string_view database,
135+
std::string_view schema,
136+
EventHandler& eventHandler);
134137

135138
/// Holds the definition of a table in a SQL database as read from the database schema.
136139
struct Table
@@ -159,7 +162,8 @@ using TableList = std::vector<Table>;
159162
using ReadAllTablesCallback = std::function<void(std::string_view /*tableName*/, size_t /*current*/, size_t /*total*/)>;
160163

161164
/// Retrieves all tables in the given @p database and @p schema.
162-
LIGHTWEIGHT_API TableList ReadAllTables(std::string_view database,
165+
LIGHTWEIGHT_API TableList ReadAllTables(SqlStatement& stmt,
166+
std::string_view database,
163167
std::string_view schema = {},
164168
ReadAllTablesCallback callback = {});
165169

@@ -170,6 +174,12 @@ LIGHTWEIGHT_API std::vector<ForeignKeyConstraint> AllForeignKeysTo(SqlStatement&
170174
LIGHTWEIGHT_API std::vector<ForeignKeyConstraint> AllForeignKeysFrom(SqlStatement& stmt,
171175
FullyQualifiedTableName const& table);
172176

177+
/// Creats an SQL CREATE TABLE plan for the given table description.
178+
LIGHTWEIGHT_API SqlCreateTablePlan MakeCreateTablePlan(Table const& tableDescription);
179+
180+
/// Creates an SQL CREATE TABLE plan for all the given table descriptions.
181+
LIGHTWEIGHT_API std::vector<SqlCreateTablePlan> MakeCreateTablePlan(TableList const& tableDescriptions);
182+
173183
} // namespace SqlSchema
174184

175185
template <>

src/tools/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,8 @@ add_executable(ddl2cpp ddl2cpp.cpp)
33
target_link_libraries(ddl2cpp PRIVATE Lightweight::Lightweight yaml-cpp)
44
target_compile_features(ddl2cpp PUBLIC cxx_std_23)
55
install(TARGETS ddl2cpp DESTINATION bin)
6+
7+
add_executable(dbcopy dbcopy.cpp)
8+
target_link_libraries(dbcopy PRIVATE Lightweight::Lightweight)
9+
target_compile_features(dbcopy PUBLIC cxx_std_23)
10+
install(TARGETS dbcopy DESTINATION bin)

src/tools/dbcopy.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include "utils.hpp"
4+
5+
#include <Lightweight/Lightweight.hpp>
6+
7+
#include <print>
8+
9+
using namespace std::string_view_literals;
10+
11+
struct Configuration
12+
{
13+
std::string from;
14+
std::string schema;
15+
std::string to;
16+
bool help = false;
17+
bool quiet = false;
18+
};
19+
20+
namespace
21+
{
22+
23+
void PrintHelp()
24+
{
25+
std::println("dbcopy [--help] [--quiet] --from=DSN [--schema=NAME] --to=DSN");
26+
std::println();
27+
}
28+
29+
std::vector<std::string> ToSql(SqlQueryFormatter const& formatter,
30+
std::vector<SqlCreateTablePlan> const& createTableMigrationPlan)
31+
{
32+
auto result = std::vector<std::string> {};
33+
for (SqlCreateTablePlan const& createTablePlan: createTableMigrationPlan)
34+
std::ranges::move(ToSql(formatter, SqlMigrationPlanElement { createTablePlan }), std::back_inserter(result));
35+
return result;
36+
}
37+
38+
} // end namespace
39+
40+
int main(int argc, char const* argv[])
41+
{
42+
auto config = Configuration {};
43+
if (!ParseCommandLineArguments(&config, argc, argv))
44+
{
45+
std::println("Error: Invalid command line arguments");
46+
return EXIT_FAILURE;
47+
}
48+
49+
if (config.help)
50+
{
51+
PrintHelp();
52+
return EXIT_SUCCESS;
53+
}
54+
55+
auto const sourceConnectionString = SqlConnectionString { config.from };
56+
auto sourceConnection = SqlConnection { sourceConnectionString };
57+
58+
SqlStatement sourceStmt { sourceConnection };
59+
SqlSchema::TableList tableSchemas =
60+
SqlSchema::ReadAllTables(sourceStmt, {}, config.schema, [](std::string_view table, size_t current, size_t total) {
61+
std::println("Processing table {}/{}: {}", current, total, table);
62+
});
63+
std::println("Read {} tables from source database", tableSchemas.size());
64+
65+
auto const createTableMigrationPlan = MakeCreateTablePlan(tableSchemas);
66+
auto const sqlTargetServerType = SqlServerType::SQLITE; // TODO(pr) determine automatically
67+
auto const& formatter = *SqlQueryFormatter::Get(sqlTargetServerType);
68+
auto const sqlQueries = ToSql(formatter, createTableMigrationPlan);
69+
70+
// Dump all SQL queries to stdout for now
71+
// TODO(pr): make this a command line option later
72+
for (auto const& sqlQuery: sqlQueries)
73+
{
74+
std::println("{}\n", sqlQuery);
75+
}
76+
77+
// auto targetConnection = SqlConnection { SqlConnectionString { config.to } };
78+
79+
return EXIT_SUCCESS;
80+
}

src/tools/ddl2cpp.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,8 +1105,9 @@ int main(int argc, char const* argv[])
11051105
PrintInfo(config);
11061106

11071107
std::vector<SqlSchema::Table> const tables = TimedExecution("Reading all tables", [&] {
1108+
SqlStatement stmt;
11081109
return SqlSchema::ReadAllTables(
1109-
config.database, config.schema, [](std::string_view tableName, size_t current, size_t total) {
1110+
stmt, config.database, config.schema, [](std::string_view tableName, size_t current, size_t total) {
11101111
std::print("\r\033[K {:>3}% [{}/{}] Reading table schema {}",
11111112
static_cast<int>((current * 100) / total),
11121113
current,

0 commit comments

Comments
 (0)