Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Parpart <[email protected]>
  • Loading branch information
christianparpart committed Jan 26, 2025
1 parent a6b6d23 commit a1b24d0
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 43 deletions.
19 changes: 19 additions & 0 deletions .vimspector.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
{
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#",
"configurations": {
"dbtool": {
"adapter": "vscode-cpptools",
"configuration": {
"request": "launch",
"program": "${workspaceRoot}/out/build/linux-gcc-debug/src/tools/dbtool",
"args": [
],
"cwd": "${workspaceRoot}",
"externalConsole": true,
"stopAtEntry": false,
"MIMode": "gdb"
},
"breakpoints": {
"exception": {
"caught": "Y",
"uncaught": "Y"
}
}
},
"CoreTest - SQLite": {
"adapter": "vscode-cpptools",
"configuration": {
Expand Down
46 changes: 46 additions & 0 deletions src/Lightweight/SqlColumnTypeDefinitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

#pragma once

#include <optional>
#include <variant>

#include <sql.h>
#include <sqlext.h>

namespace SqlColumnTypeDefinitions
{

Expand Down Expand Up @@ -50,3 +54,45 @@ using SqlColumnTypeDefinition = std::variant<SqlColumnTypeDefinitions::Bigint,
SqlColumnTypeDefinitions::Timestamp,
SqlColumnTypeDefinitions::VarBinary,
SqlColumnTypeDefinitions::Varchar>;

constexpr std::optional<SqlColumnTypeDefinition> CreateColumnTypeFromNative(int value,
std::size_t size,
std::size_t precision)
{
// Maps ODBC data types to SqlColumnTypeDefinition
// See: https://learn.microsoft.com/en-us/sql/odbc/reference/appendixes/sql-data-types?view=sql-server-ver16
using namespace SqlColumnTypeDefinitions;
// clang-format off
switch (value)
{
case SQL_BIGINT: return Bigint {};
case SQL_BINARY: return Binary { size };
case SQL_BIT: return Bool {};
case SQL_CHAR: return Char { size };
case SQL_DATE: return Date {};
case SQL_DECIMAL: return Decimal { .precision = precision, .scale = size };
case SQL_DOUBLE: return Real {};
case SQL_FLOAT: return Real {};
case SQL_GUID: return Guid {};
case SQL_INTEGER: return Integer {};
case SQL_LONGVARBINARY: return VarBinary { size };
case SQL_LONGVARCHAR: return Varchar { size };
case SQL_NUMERIC: return Decimal { .precision = precision, .scale = size };
case SQL_REAL: return Real {};
case SQL_SMALLINT: return Smallint {};
case SQL_TIME: return Time {};
case SQL_TIMESTAMP: return DateTime {};
case SQL_TINYINT: return Tinyint {};
case SQL_TYPE_DATE: return Date {};
case SQL_TYPE_TIME: return Time {};
case SQL_TYPE_TIMESTAMP: return DateTime {};
case SQL_VARBINARY: return Binary { size };
case SQL_VARCHAR: return Varchar { size };
case SQL_WCHAR: return NChar { size };
case SQL_WLONGVARCHAR: return NVarchar { size };
case SQL_WVARCHAR: return NVarchar { size };
case SQL_UNKNOWN_TYPE: return std::nullopt;
default: return std::nullopt;
}
// clang-format on
}
51 changes: 8 additions & 43 deletions src/Lightweight/SqlSchema.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: Apache-2.0

#include "SqlColumnTypeDefinitions.hpp"
#include "SqlConnection.hpp"
#include "SqlError.hpp"
#include "SqlSchema.hpp"
Expand Down Expand Up @@ -28,48 +29,6 @@ bool operator<(KeyPair const& a, KeyPair const& b)

namespace
{
SqlColumnTypeDefinition FromNativeDataType(int value, size_t size, size_t precision)
{
// Maps ODBC data types to SqlColumnTypeDefinition
// See: https://learn.microsoft.com/en-us/sql/odbc/reference/appendixes/sql-data-types?view=sql-server-ver16
using namespace SqlColumnTypeDefinitions;
// clang-format off
switch (value)
{
case SQL_BIGINT: return Bigint {};
case SQL_BINARY: return Binary { size };
case SQL_BIT: return Bool {};
case SQL_CHAR: return Char { size };
case SQL_DATE: return Date {};
case SQL_DECIMAL: assert(size <= precision); return Decimal { .precision = precision, .scale = size };
case SQL_DOUBLE: return Real {};
case SQL_FLOAT: return Real {};
case SQL_GUID: return Guid {};
case SQL_INTEGER: return Integer {};
case SQL_LONGVARBINARY: return VarBinary { size };
case SQL_LONGVARCHAR: return Varchar { size };
case SQL_NUMERIC: assert(size <= precision); return Decimal { .precision = precision, .scale = size };
case SQL_REAL: return Real {};
case SQL_SMALLINT: return Smallint {};
case SQL_TIME: return Time {};
case SQL_TIMESTAMP: return DateTime {};
case SQL_TINYINT: return Tinyint {};
case SQL_TYPE_DATE: return Date {};
case SQL_TYPE_TIME: return Time {};
case SQL_TYPE_TIMESTAMP: return DateTime {};
case SQL_VARBINARY: return Binary { size };
case SQL_VARCHAR: return Varchar { size };
case SQL_WCHAR: return NChar { size };
case SQL_WLONGVARCHAR: return NVarchar { size };
case SQL_WVARCHAR: return NVarchar { size };
// case SQL_UNKNOWN_TYPE:
default:
SqlLogger::GetLogger().OnError(SqlError::UNSUPPORTED_TYPE);
throw std::runtime_error(std::format("Unsupported data type: {}", value));
}
// clang-format on
}

std::vector<std::string> AllTables(std::string_view database, std::string_view schema)
{
auto const tableType = "TABLE"sv;
Expand Down Expand Up @@ -252,7 +211,13 @@ void ReadAllTables(std::string_view database, std::string_view schema, EventHand
// 12 - remarks
column.defaultValue = columnStmt.GetColumn<std::string>(13);

column.type = FromNativeDataType(type, column.size, column.decimalDigits);
if (auto cType = CreateColumnTypeFromNative(type, column.size, column.decimalDigits); cType.has_value())
column.type = *cType;
else
{
SqlLogger::GetLogger().OnError(SqlError::UNSUPPORTED_TYPE);
throw std::runtime_error(std::format("Unsupported data type: {}", type));
}

// accumulated properties
column.isPrimaryKey = std::ranges::contains(primaryKeys, column.name);
Expand Down
5 changes: 5 additions & 0 deletions src/tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ add_executable(ddl2cpp ddl2cpp.cpp)
target_link_libraries(ddl2cpp PRIVATE Lightweight::Lightweight)
target_compile_features(ddl2cpp PUBLIC cxx_std_23)
install(TARGETS ddl2cpp DESTINATION bin)

add_executable(dbtool dbtool.cpp)
target_link_libraries(dbtool PRIVATE Lightweight::Lightweight)
target_compile_features(dbtool PUBLIC cxx_std_23)
install(TARGETS dbtool DESTINATION bin)
125 changes: 125 additions & 0 deletions src/tools/dbtool.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: Apache-2.0

#include "utils.hpp"

#include <Lightweight/SqlColumnTypeDefinitions.hpp>
#include <Lightweight/SqlMigration.hpp>
#include <Lightweight/SqlQuery.hpp>
#include <Lightweight/SqlSchema.hpp>

#include <cstdlib>
#include <iostream>

void showHelp()
{
// DRAFT idea on how the CLI utility could look like
// clang-format off
std::cout << "Usage: dbtool <COMMAND ...>\n"
"\n"
"dbtool is a tool for managing database migrations in C++ projects.\n"
"\n"
" dbtool help - prints this help message\n"
" dbtool version - prints the version of dbtool\n"
"\n"
" Migration tasks\n"
"\n"
" dbtool migrate - applies pending migrations to the database\n"
" dbtool list-pending - lists pending migrations\n"
" dbtool list-applied - lists applied migrations\n"
" dbtool create <NAME> - creates a new migration with the given name\n"
" dbtool apply <NAME> - applies the migration with the given name\n"
" dbtool rollback <NAME> - rolls back the migration with the given name\n"
"\n"
" Options:\n"
"\n"
" -m, --module <MODULE_NAME> - specifies the module name (DLL or .so file) to load migrations from\n"
" Migration libraries are loaded automatically relative to the executables path\n"
"\n"
" Databas eadministration tasks\n"
"\n"
" dbtool dump-schema <DSN> - Dumps the schema of a given database to stdout\n"
" dbtool backup <DSN> to <FILE> - Creates a backup of a given database\n"
" dbtool restore <FILE> to <DSN> - Restores a database from a backup file\n"
"\n"
"Examples:\n"
" dbtool create add_users_table\n"
" dbtool apply add_users_table\n"
" dbtool rollback add_users_table\n"
" dbtool migrate\n"
"\n";
// clang-format on
}

SqlMigrationQueryBuilder BuildStructureFromSchema(std::string_view databaseName,
std::string_view schemaName,
SqlQueryFormatter const& dialect)
{
auto builder = SqlMigrationQueryBuilder { dialect };
SqlSchema::TableList tables = SqlSchema::ReadAllTables(databaseName, schemaName);
for (SqlSchema::Table const& table: tables)
{
auto tableBuilder = builder.CreateTable(table.name);
for (SqlSchema::Column const& column: table.columns)
{
auto columnDeclaration = SqlColumnDeclaration {
.name = column.name,
.type = column.type,
.required = !column.isNullable,
.unique = column.isUnique,
.index = false, // TODO
};
tableBuilder.Column(std::move(columnDeclaration));
}
}
return builder;
}

using namespace std::string_literals;
using namespace std::string_view_literals;

struct DumpSchemaConfiguration
{
std::string connectionString;
std::string database = "testdb";
std::optional<std::string> schema;
bool help = false;
};

int dumpSchema(int argc, char const* argv[])
{
auto config = DumpSchemaConfiguration {};
if (!parseCommandLineArguments(&config, argc, argv))
return EXIT_FAILURE;

if (config.help)
{
showHelp();
return EXIT_SUCCESS;
}

SqlConnection::SetDefaultConnectionString(SqlConnectionString { config.connectionString });

auto const* dialect = &SqlQueryFormatter::SqlServer(); // TODO: configurable

auto createStructureStmts = BuildStructureFromSchema(config.database, config.schema.value_or(""), *dialect);
auto const planSql = createStructureStmts.GetPlan().ToSql();
for (auto const& sql: planSql)
std::cout << sql << '\n';

return EXIT_SUCCESS;
}

int main(int argc, char const* argv[])
{
if (argc <= 1 || (argv[1] == "help"sv || argv[1] == "--help"sv || argv[1] == "-h"sv))
{
showHelp();
return EXIT_FAILURE;
}

if (argv[1] == "dump-schema"sv)
return dumpSchema(argc - 2, argv + 2);

std::cerr << "Unknown command: " << argv[1] << '\n';
return EXIT_FAILURE;
}
Loading

0 comments on commit a1b24d0

Please sign in to comment.