Skip to content

Commit a1b24d0

Browse files
WIP
Signed-off-by: Christian Parpart <[email protected]>
1 parent a6b6d23 commit a1b24d0

File tree

6 files changed

+313
-43
lines changed

6 files changed

+313
-43
lines changed

.vimspector.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
{
22
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#",
33
"configurations": {
4+
"dbtool": {
5+
"adapter": "vscode-cpptools",
6+
"configuration": {
7+
"request": "launch",
8+
"program": "${workspaceRoot}/out/build/linux-gcc-debug/src/tools/dbtool",
9+
"args": [
10+
],
11+
"cwd": "${workspaceRoot}",
12+
"externalConsole": true,
13+
"stopAtEntry": false,
14+
"MIMode": "gdb"
15+
},
16+
"breakpoints": {
17+
"exception": {
18+
"caught": "Y",
19+
"uncaught": "Y"
20+
}
21+
}
22+
},
423
"CoreTest - SQLite": {
524
"adapter": "vscode-cpptools",
625
"configuration": {

src/Lightweight/SqlColumnTypeDefinitions.hpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
#pragma once
44

5+
#include <optional>
56
#include <variant>
67

8+
#include <sql.h>
9+
#include <sqlext.h>
10+
711
namespace SqlColumnTypeDefinitions
812
{
913

@@ -50,3 +54,45 @@ using SqlColumnTypeDefinition = std::variant<SqlColumnTypeDefinitions::Bigint,
5054
SqlColumnTypeDefinitions::Timestamp,
5155
SqlColumnTypeDefinitions::VarBinary,
5256
SqlColumnTypeDefinitions::Varchar>;
57+
58+
constexpr std::optional<SqlColumnTypeDefinition> CreateColumnTypeFromNative(int value,
59+
std::size_t size,
60+
std::size_t precision)
61+
{
62+
// Maps ODBC data types to SqlColumnTypeDefinition
63+
// See: https://learn.microsoft.com/en-us/sql/odbc/reference/appendixes/sql-data-types?view=sql-server-ver16
64+
using namespace SqlColumnTypeDefinitions;
65+
// clang-format off
66+
switch (value)
67+
{
68+
case SQL_BIGINT: return Bigint {};
69+
case SQL_BINARY: return Binary { size };
70+
case SQL_BIT: return Bool {};
71+
case SQL_CHAR: return Char { size };
72+
case SQL_DATE: return Date {};
73+
case SQL_DECIMAL: return Decimal { .precision = precision, .scale = size };
74+
case SQL_DOUBLE: return Real {};
75+
case SQL_FLOAT: return Real {};
76+
case SQL_GUID: return Guid {};
77+
case SQL_INTEGER: return Integer {};
78+
case SQL_LONGVARBINARY: return VarBinary { size };
79+
case SQL_LONGVARCHAR: return Varchar { size };
80+
case SQL_NUMERIC: return Decimal { .precision = precision, .scale = size };
81+
case SQL_REAL: return Real {};
82+
case SQL_SMALLINT: return Smallint {};
83+
case SQL_TIME: return Time {};
84+
case SQL_TIMESTAMP: return DateTime {};
85+
case SQL_TINYINT: return Tinyint {};
86+
case SQL_TYPE_DATE: return Date {};
87+
case SQL_TYPE_TIME: return Time {};
88+
case SQL_TYPE_TIMESTAMP: return DateTime {};
89+
case SQL_VARBINARY: return Binary { size };
90+
case SQL_VARCHAR: return Varchar { size };
91+
case SQL_WCHAR: return NChar { size };
92+
case SQL_WLONGVARCHAR: return NVarchar { size };
93+
case SQL_WVARCHAR: return NVarchar { size };
94+
case SQL_UNKNOWN_TYPE: return std::nullopt;
95+
default: return std::nullopt;
96+
}
97+
// clang-format on
98+
}

src/Lightweight/SqlSchema.cpp

Lines changed: 8 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// SPDX-License-Identifier: Apache-2.0
22

3+
#include "SqlColumnTypeDefinitions.hpp"
34
#include "SqlConnection.hpp"
45
#include "SqlError.hpp"
56
#include "SqlSchema.hpp"
@@ -28,48 +29,6 @@ bool operator<(KeyPair const& a, KeyPair const& b)
2829

2930
namespace
3031
{
31-
SqlColumnTypeDefinition FromNativeDataType(int value, size_t size, size_t precision)
32-
{
33-
// Maps ODBC data types to SqlColumnTypeDefinition
34-
// See: https://learn.microsoft.com/en-us/sql/odbc/reference/appendixes/sql-data-types?view=sql-server-ver16
35-
using namespace SqlColumnTypeDefinitions;
36-
// clang-format off
37-
switch (value)
38-
{
39-
case SQL_BIGINT: return Bigint {};
40-
case SQL_BINARY: return Binary { size };
41-
case SQL_BIT: return Bool {};
42-
case SQL_CHAR: return Char { size };
43-
case SQL_DATE: return Date {};
44-
case SQL_DECIMAL: assert(size <= precision); return Decimal { .precision = precision, .scale = size };
45-
case SQL_DOUBLE: return Real {};
46-
case SQL_FLOAT: return Real {};
47-
case SQL_GUID: return Guid {};
48-
case SQL_INTEGER: return Integer {};
49-
case SQL_LONGVARBINARY: return VarBinary { size };
50-
case SQL_LONGVARCHAR: return Varchar { size };
51-
case SQL_NUMERIC: assert(size <= precision); return Decimal { .precision = precision, .scale = size };
52-
case SQL_REAL: return Real {};
53-
case SQL_SMALLINT: return Smallint {};
54-
case SQL_TIME: return Time {};
55-
case SQL_TIMESTAMP: return DateTime {};
56-
case SQL_TINYINT: return Tinyint {};
57-
case SQL_TYPE_DATE: return Date {};
58-
case SQL_TYPE_TIME: return Time {};
59-
case SQL_TYPE_TIMESTAMP: return DateTime {};
60-
case SQL_VARBINARY: return Binary { size };
61-
case SQL_VARCHAR: return Varchar { size };
62-
case SQL_WCHAR: return NChar { size };
63-
case SQL_WLONGVARCHAR: return NVarchar { size };
64-
case SQL_WVARCHAR: return NVarchar { size };
65-
// case SQL_UNKNOWN_TYPE:
66-
default:
67-
SqlLogger::GetLogger().OnError(SqlError::UNSUPPORTED_TYPE);
68-
throw std::runtime_error(std::format("Unsupported data type: {}", value));
69-
}
70-
// clang-format on
71-
}
72-
7332
std::vector<std::string> AllTables(std::string_view database, std::string_view schema)
7433
{
7534
auto const tableType = "TABLE"sv;
@@ -252,7 +211,13 @@ void ReadAllTables(std::string_view database, std::string_view schema, EventHand
252211
// 12 - remarks
253212
column.defaultValue = columnStmt.GetColumn<std::string>(13);
254213

255-
column.type = FromNativeDataType(type, column.size, column.decimalDigits);
214+
if (auto cType = CreateColumnTypeFromNative(type, column.size, column.decimalDigits); cType.has_value())
215+
column.type = *cType;
216+
else
217+
{
218+
SqlLogger::GetLogger().OnError(SqlError::UNSUPPORTED_TYPE);
219+
throw std::runtime_error(std::format("Unsupported data type: {}", type));
220+
}
256221

257222
// accumulated properties
258223
column.isPrimaryKey = std::ranges::contains(primaryKeys, column.name);

src/tools/CMakeLists.txt

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

src/tools/dbtool.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include "utils.hpp"
4+
5+
#include <Lightweight/SqlColumnTypeDefinitions.hpp>
6+
#include <Lightweight/SqlMigration.hpp>
7+
#include <Lightweight/SqlQuery.hpp>
8+
#include <Lightweight/SqlSchema.hpp>
9+
10+
#include <cstdlib>
11+
#include <iostream>
12+
13+
void showHelp()
14+
{
15+
// DRAFT idea on how the CLI utility could look like
16+
// clang-format off
17+
std::cout << "Usage: dbtool <COMMAND ...>\n"
18+
"\n"
19+
"dbtool is a tool for managing database migrations in C++ projects.\n"
20+
"\n"
21+
" dbtool help - prints this help message\n"
22+
" dbtool version - prints the version of dbtool\n"
23+
"\n"
24+
" Migration tasks\n"
25+
"\n"
26+
" dbtool migrate - applies pending migrations to the database\n"
27+
" dbtool list-pending - lists pending migrations\n"
28+
" dbtool list-applied - lists applied migrations\n"
29+
" dbtool create <NAME> - creates a new migration with the given name\n"
30+
" dbtool apply <NAME> - applies the migration with the given name\n"
31+
" dbtool rollback <NAME> - rolls back the migration with the given name\n"
32+
"\n"
33+
" Options:\n"
34+
"\n"
35+
" -m, --module <MODULE_NAME> - specifies the module name (DLL or .so file) to load migrations from\n"
36+
" Migration libraries are loaded automatically relative to the executables path\n"
37+
"\n"
38+
" Databas eadministration tasks\n"
39+
"\n"
40+
" dbtool dump-schema <DSN> - Dumps the schema of a given database to stdout\n"
41+
" dbtool backup <DSN> to <FILE> - Creates a backup of a given database\n"
42+
" dbtool restore <FILE> to <DSN> - Restores a database from a backup file\n"
43+
"\n"
44+
"Examples:\n"
45+
" dbtool create add_users_table\n"
46+
" dbtool apply add_users_table\n"
47+
" dbtool rollback add_users_table\n"
48+
" dbtool migrate\n"
49+
"\n";
50+
// clang-format on
51+
}
52+
53+
SqlMigrationQueryBuilder BuildStructureFromSchema(std::string_view databaseName,
54+
std::string_view schemaName,
55+
SqlQueryFormatter const& dialect)
56+
{
57+
auto builder = SqlMigrationQueryBuilder { dialect };
58+
SqlSchema::TableList tables = SqlSchema::ReadAllTables(databaseName, schemaName);
59+
for (SqlSchema::Table const& table: tables)
60+
{
61+
auto tableBuilder = builder.CreateTable(table.name);
62+
for (SqlSchema::Column const& column: table.columns)
63+
{
64+
auto columnDeclaration = SqlColumnDeclaration {
65+
.name = column.name,
66+
.type = column.type,
67+
.required = !column.isNullable,
68+
.unique = column.isUnique,
69+
.index = false, // TODO
70+
};
71+
tableBuilder.Column(std::move(columnDeclaration));
72+
}
73+
}
74+
return builder;
75+
}
76+
77+
using namespace std::string_literals;
78+
using namespace std::string_view_literals;
79+
80+
struct DumpSchemaConfiguration
81+
{
82+
std::string connectionString;
83+
std::string database = "testdb";
84+
std::optional<std::string> schema;
85+
bool help = false;
86+
};
87+
88+
int dumpSchema(int argc, char const* argv[])
89+
{
90+
auto config = DumpSchemaConfiguration {};
91+
if (!parseCommandLineArguments(&config, argc, argv))
92+
return EXIT_FAILURE;
93+
94+
if (config.help)
95+
{
96+
showHelp();
97+
return EXIT_SUCCESS;
98+
}
99+
100+
SqlConnection::SetDefaultConnectionString(SqlConnectionString { config.connectionString });
101+
102+
auto const* dialect = &SqlQueryFormatter::SqlServer(); // TODO: configurable
103+
104+
auto createStructureStmts = BuildStructureFromSchema(config.database, config.schema.value_or(""), *dialect);
105+
auto const planSql = createStructureStmts.GetPlan().ToSql();
106+
for (auto const& sql: planSql)
107+
std::cout << sql << '\n';
108+
109+
return EXIT_SUCCESS;
110+
}
111+
112+
int main(int argc, char const* argv[])
113+
{
114+
if (argc <= 1 || (argv[1] == "help"sv || argv[1] == "--help"sv || argv[1] == "-h"sv))
115+
{
116+
showHelp();
117+
return EXIT_FAILURE;
118+
}
119+
120+
if (argv[1] == "dump-schema"sv)
121+
return dumpSchema(argc - 2, argv + 2);
122+
123+
std::cerr << "Unknown command: " << argv[1] << '\n';
124+
return EXIT_FAILURE;
125+
}

0 commit comments

Comments
 (0)