From 66d9272886fee47a1bbb644cf4f171f007e11c92 Mon Sep 17 00:00:00 2001 From: Frank Ueberschar Date: Thu, 20 Feb 2020 14:57:52 +0100 Subject: [PATCH] dbcopy: use insert mode with -i from command line interface - moved the data converters from column description to the regarding database export/import class module - add command line option -i --- core/src/cats/postgresql_batch.cc | 9 +- core/src/dird/dbcopy/column_description.cc | 91 +------ core/src/dird/dbcopy/column_description.h | 34 +-- .../dbcopy/database_column_descriptions.cc | 9 +- core/src/dird/dbcopy/database_export.cc | 6 +- core/src/dird/dbcopy/database_export.h | 10 +- .../dird/dbcopy/database_export_postgresql.cc | 226 +++++++++++++++--- .../dird/dbcopy/database_export_postgresql.h | 9 +- core/src/dird/dbcopy/database_import_mysql.cc | 22 +- .../dbcopy/database_table_descriptions.cc | 10 + .../dird/dbcopy/database_table_descriptions.h | 2 + core/src/dird/dbcopy/dbcopy.cc | 27 ++- 12 files changed, 280 insertions(+), 175 deletions(-) diff --git a/core/src/cats/postgresql_batch.cc b/core/src/cats/postgresql_batch.cc index d3dc755c19f..78d0d776d2a 100644 --- a/core/src/cats/postgresql_batch.cc +++ b/core/src/cats/postgresql_batch.cc @@ -238,6 +238,11 @@ bool BareosDbPostgresql::SqlBatchInsertFileTable(JobControlRecord* jcr, return true; } + +/* ************************************* * + * ** Generic SQL Copy used by dbcopy ** * + * ************************************* */ + class CleanupResult { public: CleanupResult(PGresult** r, int* s) : result(r), status(s) {} @@ -279,7 +284,7 @@ bool BareosDbPostgresql::SqlCopyStart( query.resize(query.size() - 2); query += ") FROM STDIN WITH (" - " FORMAT csv" + " FORMAT text" ", DELIMITER '\t'" ")"; @@ -318,7 +323,7 @@ bool BareosDbPostgresql::SqlCopyInsert(const std::vector& columns) std::vector buffer; for (const auto& column : columns) { if (strlen(column.data_pointer) == 0) { - query += "\"\"NULL\"\""; + query += ""; } else { buffer.resize(strlen(column.data_pointer) * 2 + 1); pgsql_copy_escape(buffer.data(), column.data_pointer, buffer.size()); diff --git a/core/src/dird/dbcopy/column_description.cc b/core/src/dird/dbcopy/column_description.cc index 19b4d46e59d..5484983f0b9 100644 --- a/core/src/dird/dbcopy/column_description.cc +++ b/core/src/dird/dbcopy/column_description.cc @@ -47,97 +47,14 @@ ColumnDescription::ColumnDescription(const char* column_name_in, } } -static void no_conversion(BareosDb* db, ColumnData& fd) -{ - fd.data_pointer = fd.data_pointer ? fd.data_pointer : ""; -} - -static void timestamp_conversion_postgresql(BareosDb* db, ColumnData& fd) -{ - static const char* dummy_timepoint = "1970-01-01 00:00:00"; - if (!fd.data_pointer) { - fd.data_pointer = dummy_timepoint; - } else if (fd.data_pointer[0] == '0') { - fd.data_pointer = dummy_timepoint; - } else if (strlen(fd.data_pointer) == 0) { - fd.data_pointer = dummy_timepoint; - } -} - -static void string_conversion_postgresql(BareosDb* db, ColumnData& fd) -{ - if (fd.data_pointer) { - std::size_t len{strlen(fd.data_pointer)}; - fd.converted_data.resize(len * 2 + 1); - db->EscapeString(nullptr, fd.converted_data.data(), fd.data_pointer, len); - fd.data_pointer = fd.converted_data.data(); - } else { - fd.data_pointer = ""; - } -} - -static void bytea_conversion_postgresql(BareosDb* db, ColumnData& fd) -{ - std::size_t new_len{}; - std::size_t old_len = fd.size; - - auto old = reinterpret_cast(fd.data_pointer); - - auto obj = db->EscapeObject(old, old_len, new_len); - - fd.converted_data.resize(new_len + 1); - memcpy(fd.converted_data.data(), obj, new_len + 1); - - db->FreeEscapedObjectMemory(obj); - - fd.data_pointer = fd.converted_data.data(); -} - -const DataTypeConverterMap ColumnDescriptionMysql::db_import_converter_map{ - {"bigint", no_conversion}, {"binary", no_conversion}, - {"blob", no_conversion}, {"char", no_conversion}, - {"datetime", no_conversion}, {"decimal", no_conversion}, - {"enum", no_conversion}, {"int", no_conversion}, - {"longblob", no_conversion}, {"smallint", no_conversion}, - {"text", no_conversion}, {"timestamp", no_conversion}, - {"tinyblob", no_conversion}, {"tinyint", no_conversion}, - {"varchar", no_conversion}}; - -ColumnDescriptionMysql::ColumnDescriptionMysql(const char* column_name_in, - const char* data_type_in, - const char* max_length_in) - : ColumnDescription(column_name_in, data_type_in, max_length_in) -{ - try { - db_import_converter = db_import_converter_map.at(data_type_in); - } catch (const std::out_of_range& e) { - std::string err{"Mysql: Data type not found in conversion map: "}; - err += data_type_in; - throw std::runtime_error(err); - } -} - -const DataTypeConverterMap ColumnDescriptionPostgresql::db_export_converter_map{ - {"bigint", no_conversion}, - {"bytea", bytea_conversion_postgresql}, - {"character", string_conversion_postgresql}, - {"integer", no_conversion}, - {"numeric", no_conversion}, - {"smallint", no_conversion}, - {"text", string_conversion_postgresql}, - {"timestamp without time zone", timestamp_conversion_postgresql}}; - -ColumnDescriptionPostgresql::ColumnDescriptionPostgresql( - const char* column_name_in, - const char* data_type_in, - const char* max_length_in) - : ColumnDescription(column_name_in, data_type_in, max_length_in) +void ColumnDescription::RegisterConverterCallbackFromMap( + const DataTypeConverterMap& converter_map) { try { - db_export_converter = db_export_converter_map.at(data_type_in); + converter = converter_map.at(data_type); } catch (const std::out_of_range& e) { std::string err{"Postgresql: Data type not found in conversion map: "}; - err += data_type_in; + err += data_type; throw std::runtime_error(err); } } diff --git a/core/src/dird/dbcopy/column_description.h b/core/src/dird/dbcopy/column_description.h index 51dffa945a7..e7a81c6a858 100644 --- a/core/src/dird/dbcopy/column_description.h +++ b/core/src/dird/dbcopy/column_description.h @@ -35,38 +35,18 @@ class ColumnDescription { const char* data_type_in, const char* max_length_in); virtual ~ColumnDescription() = default; - - std::string column_name; - std::string data_type; - std::size_t character_maximum_length{}; + ColumnDescription() = delete; using ConverterCallback = std::function; - ConverterCallback db_export_converter{}; - ConverterCallback db_import_converter{}; -}; - -using DataTypeConverterMap = - std::map; - -class ColumnDescriptionMysql : public ColumnDescription { - public: - ColumnDescriptionMysql(const char* column_name_in, - const char* data_type_in, - const char* max_length_in); + using DataTypeConverterMap = std::map; - private: - static const DataTypeConverterMap db_import_converter_map; -}; + void RegisterConverterCallbackFromMap(const DataTypeConverterMap&); -class ColumnDescriptionPostgresql : public ColumnDescription { - public: - ColumnDescriptionPostgresql(const char* column_name_in, - const char* data_type_in, - const char* max_length_in); + std::string column_name; + std::string data_type; + std::size_t character_maximum_length{}; - private: - static const DataTypeConverterMap db_export_converter_map; + ConverterCallback converter{}; }; - #endif // BAREOS_SRC_DIRD_DBCOPY_COLUMN_DESCRIPTION_H_ diff --git a/core/src/dird/dbcopy/database_column_descriptions.cc b/core/src/dird/dbcopy/database_column_descriptions.cc index d3304d6e82d..44e8be1ddfc 100644 --- a/core/src/dird/dbcopy/database_column_descriptions.cc +++ b/core/src/dird/dbcopy/database_column_descriptions.cc @@ -56,10 +56,9 @@ int DatabaseColumnDescriptionsPostgresql::ResultHandler(void* ctx, { auto t = static_cast(ctx); - t->column_descriptions.emplace_back( - std::make_unique( - row[RowIndex::kColumnName], row[RowIndex::kDataType], - row[RowIndex::kCharMaxLenght])); + t->column_descriptions.emplace_back(std::make_unique( + row[RowIndex::kColumnName], row[RowIndex::kDataType], + row[RowIndex::kCharMaxLenght])); return 0; } @@ -87,7 +86,7 @@ int DatabaseColumnDescriptionsMysql::ResultHandler(void* ctx, { auto t = static_cast(ctx); - t->column_descriptions.emplace_back(std::make_unique( + t->column_descriptions.emplace_back(std::make_unique( row[RowIndex::kColumnName], row[RowIndex::kDataType], row[RowIndex::kCharMaxLenght])); diff --git a/core/src/dird/dbcopy/database_export.cc b/core/src/dird/dbcopy/database_export.cc index 5ce6108c0ac..84d7e766291 100644 --- a/core/src/dird/dbcopy/database_export.cc +++ b/core/src/dird/dbcopy/database_export.cc @@ -26,8 +26,7 @@ #include -DatabaseExport::DatabaseExport(const DatabaseConnection& db_connection, - bool clear_tables) +DatabaseExport::DatabaseExport(const DatabaseConnection& db_connection) : db_(db_connection.db) , table_descriptions_(DatabaseTableDescriptions::Create(db_connection)) { @@ -38,11 +37,12 @@ DatabaseExport::~DatabaseExport() = default; std::unique_ptr DatabaseExport::Create( const DatabaseConnection& db_connection, + DatabaseExport::InsertMode mode, bool clear_tables) { switch (db_connection.db_type) { case DatabaseType::Enum::kPostgresql: - return std::make_unique(db_connection, + return std::make_unique(db_connection, mode, clear_tables); default: case DatabaseType::Enum::kMysql: diff --git a/core/src/dird/dbcopy/database_export.h b/core/src/dird/dbcopy/database_export.h index b5649f52e9c..173e996c8bc 100644 --- a/core/src/dird/dbcopy/database_export.h +++ b/core/src/dird/dbcopy/database_export.h @@ -32,12 +32,18 @@ class DatabaseTableDescriptions; class DatabaseExport { public: - DatabaseExport(const DatabaseConnection& db_connection, - bool clear_tables = false); + DatabaseExport(const DatabaseConnection& db_connection); virtual ~DatabaseExport(); + enum class InsertMode + { + kSqlInsert, + kSqlCopy + }; + static std::unique_ptr Create( const DatabaseConnection& db_connection, + InsertMode mode = InsertMode::kSqlCopy, bool clear_tables = false); virtual bool StartTable(const std::string& table_name) = 0; diff --git a/core/src/dird/dbcopy/database_export_postgresql.cc b/core/src/dird/dbcopy/database_export_postgresql.cc index f403e172d1a..ced8212970f 100644 --- a/core/src/dird/dbcopy/database_export_postgresql.cc +++ b/core/src/dird/dbcopy/database_export_postgresql.cc @@ -28,12 +28,109 @@ #include #include +#include + +static void no_conversion(BareosDb* db, ColumnData& fd) +{ + fd.data_pointer = fd.data_pointer ? fd.data_pointer : ""; +} + +static void timestamp_conversion_postgresql(BareosDb* db, ColumnData& fd) +{ + static const char* dummy_timepoint = "1970-01-01 00:00:00"; + if (!fd.data_pointer) { + fd.data_pointer = dummy_timepoint; + } else if (fd.data_pointer[0] == '0') { + fd.data_pointer = dummy_timepoint; + } else if (strlen(fd.data_pointer) == 0) { + fd.data_pointer = dummy_timepoint; + } +} + +static void string_conversion_postgresql(BareosDb* db, ColumnData& fd) +{ + if (fd.data_pointer) { + std::size_t len{strlen(fd.data_pointer)}; + fd.converted_data.resize(len * 2 + 1); + db->EscapeString(nullptr, fd.converted_data.data(), fd.data_pointer, len); + fd.data_pointer = fd.converted_data.data(); + } else { + fd.data_pointer = ""; + } +} + +static void bytea_conversion_postgresql(BareosDb* db, ColumnData& fd) +{ + std::size_t new_len{}; + std::size_t old_len = fd.length_of_restore_object; + + auto old = reinterpret_cast(fd.data_pointer); + + auto obj = db->EscapeObject(old, old_len, new_len); + + fd.converted_data.resize(new_len + 1); + memcpy(fd.converted_data.data(), obj, new_len + 1); + + db->FreeEscapedObjectMemory(obj); + + fd.data_pointer = fd.converted_data.data(); +} + +static const ColumnDescription::DataTypeConverterMap + db_export_converter_map_postgres_sql_insert{ + {"bigint", no_conversion}, + {"bytea", bytea_conversion_postgresql}, + {"character", string_conversion_postgresql}, + {"integer", no_conversion}, + {"numeric", no_conversion}, + {"smallint", no_conversion}, + {"text", string_conversion_postgresql}, + {"timestamp without time zone", timestamp_conversion_postgresql}}; + + +static const ColumnDescription::DataTypeConverterMap + db_export_converter_map_postgres_sql_copy{ + {"bigint", no_conversion}, + {"bytea", bytea_conversion_postgresql}, + {"character", no_conversion}, + {"integer", no_conversion}, + {"numeric", no_conversion}, + {"smallint", no_conversion}, + {"text", no_conversion}, + {"timestamp without time zone", timestamp_conversion_postgresql}}; + +bool DatabaseExportPostgresql::UseCopyInsertion() +{ + switch (insert_mode_) { + case DatabaseExport::InsertMode::kSqlCopy: + return true; + default: + case DatabaseExport::InsertMode::kSqlInsert: + return false; + } +} DatabaseExportPostgresql::DatabaseExportPostgresql( const DatabaseConnection& db_connection, + DatabaseExport::InsertMode mode, bool clear_tables) - : DatabaseExport(db_connection, clear_tables) + : DatabaseExport(db_connection), insert_mode_(mode) { + switch (insert_mode_) { + case DatabaseExport::InsertMode::kSqlCopy: + std::cout << "--> insert mode: Sql Copy" << std::endl; + table_descriptions_->SetAllConverterCallbacks( + db_export_converter_map_postgres_sql_copy); + break; + case DatabaseExport::InsertMode::kSqlInsert: + std::cout << "--> insert mode: Sql Insert" << std::endl; + table_descriptions_->SetAllConverterCallbacks( + db_export_converter_map_postgres_sql_insert); + break; + default: + throw std::runtime_error("Unknown type for export"); + } + if (clear_tables) { for (const auto& t : table_descriptions_->tables) { if (t.table_name != "version") { @@ -61,50 +158,80 @@ DatabaseExportPostgresql::~DatabaseExportPostgresql() void DatabaseExportPostgresql::CopyRow(RowData& source_data_row) { - std::string query_into{"INSERT INTO "}; - query_into += source_data_row.table_name; - query_into += " ("; - - std::string query_values = " VALUES ("; - - for (std::size_t i = 0; i < source_data_row.column_descriptions.size(); i++) { - const ColumnDescription* column_description = - table_descriptions_->GetColumnDescription( - source_data_row.table_name, - source_data_row.column_descriptions[i]->column_name); + if (UseCopyInsertion()) { + for (std::size_t i = 0; i < source_data_row.column_descriptions.size(); + i++) { + const ColumnDescription* column_description = + table_descriptions_->GetColumnDescription( + source_data_row.table_name, + source_data_row.column_descriptions[i]->column_name); + + if (!column_description) { + std::string err{"Could not get column description for: "}; + err += source_data_row.column_descriptions[i]->column_name; + throw std::runtime_error(err); + } - if (!column_description) { - std::string err{"Could not get column description for: "}; - err += source_data_row.column_descriptions[i]->column_name; + if (i < source_data_row.columns.size()) { + column_description->converter(db_, source_data_row.columns[i]); + } else { + throw std::runtime_error( + "Row number does not match column description"); + } + } + if (!db_->SqlCopyInsert(source_data_row.columns)) { + std::string err{"DatabaseExportPostgresql: Could not execute query: "}; + err += db_->get_errmsg(); throw std::runtime_error(err); } + } else { // ! UseCopyInsertion + std::string query_into{"INSERT INTO "}; + query_into += source_data_row.table_name; + query_into += " ("; + + std::string query_values = " VALUES ("; + + for (std::size_t i = 0; i < source_data_row.column_descriptions.size(); + i++) { + const ColumnDescription* column_description = + table_descriptions_->GetColumnDescription( + source_data_row.table_name, + source_data_row.column_descriptions[i]->column_name); + + if (!column_description) { + std::string err{"Could not get column description for: "}; + err += source_data_row.column_descriptions[i]->column_name; + throw std::runtime_error(err); + } - query_into += column_description->column_name; - query_into += ", "; + query_into += column_description->column_name; + query_into += ", "; - if (i < source_data_row.columns.size()) { - query_values += "'"; - column_description->db_export_converter(db_, source_data_row.columns[i]); - query_values += source_data_row.columns[i].data_pointer; - query_values += "',"; - } else { - throw std::runtime_error("Row number does not match column description"); + if (i < source_data_row.columns.size()) { + query_values += "'"; + column_description->converter(db_, source_data_row.columns[i]); + query_values += source_data_row.columns[i].data_pointer; + query_values += "',"; + } else { + throw std::runtime_error( + "Row number does not match column description"); + } } - } - query_values.resize(query_values.size() - 1); - query_values += ")"; + query_values.resize(query_values.size() - 1); + query_values += ")"; - query_into.resize(query_into.size() - 2); - query_into += ")"; + query_into.resize(query_into.size() - 2); + query_into += ")"; - query_into += query_values; + query_into += query_values; - if (!db_->SqlQuery(query_into.c_str())) { - std::string err{"DatabaseExportPostgresql: Could not execute query: "}; - err += db_->get_errmsg(); - throw std::runtime_error(err); - } + if (!db_->SqlQuery(query_into.c_str())) { + std::string err{"DatabaseExportPostgresql: Could not execute query: "}; + err += db_->get_errmsg(); + throw std::runtime_error(err); + } + } // if(UseCopyInsertion()) } void DatabaseExportPostgresql::CopyStart() @@ -189,10 +316,17 @@ static bool TableIsEmtpy(BareosDb* db, const std::string& table_name) return true; } -bool DatabaseExportPostgresql::TableExists(const std::string& table_name) +bool DatabaseExportPostgresql::TableExists( + const std::string& table_name, + std::vector& column_names) { auto found = table_descriptions_->GetTableDescription(table_name); if (found == nullptr) { return false; } + + for (const auto& col : found->column_descriptions) { + column_names.push_back(col->column_name); + } + return true; } @@ -200,7 +334,10 @@ bool DatabaseExportPostgresql::StartTable(const std::string& table_name) { std::cout << "====== table " << table_name << " ======" << std::endl; std::cout << "--> checking destination table..." << std::endl; - if (!TableExists(table_name)) { + + std::vector column_names; + + if (!TableExists(table_name, column_names)) { std::cout << "--> destination table does not exist" << std::endl; return false; } @@ -215,12 +352,25 @@ bool DatabaseExportPostgresql::StartTable(const std::string& table_name) cursor_start_new_table_ = true; } + if (UseCopyInsertion()) { + if (!db_->SqlCopyStart(table_name, column_names)) { + std::string err{"Could not start Sql Copy: "}; + err += db_->get_errmsg(); + throw std::runtime_error(err); + } + } return true; } void DatabaseExportPostgresql::EndTable(const std::string& table_name) { - if (table_name == "RestoreObject") { + if (UseCopyInsertion()) { + if (!db_->SqlCopyEnd()) { + std::string err{"Could not end Sql Copy: "}; + err += db_->get_errmsg(); + throw std::runtime_error(err); + } + } else if (table_name == "RestoreObject") { std::string query{ "UPDATE restoreobject SET objectlength=length(restoreobject)"}; if (!db_->SqlQuery(query.c_str())) { diff --git a/core/src/dird/dbcopy/database_export_postgresql.h b/core/src/dird/dbcopy/database_export_postgresql.h index feff8fd66f4..c26d944ba6a 100644 --- a/core/src/dird/dbcopy/database_export_postgresql.h +++ b/core/src/dird/dbcopy/database_export_postgresql.h @@ -22,12 +22,16 @@ #ifndef BAREOS_SRC_DIRD_DBCOPY_DATABASE_EXPORT_POSTGRESQL_H_ #define BAREOS_SRC_DIRD_DBCOPY_DATABASE_EXPORT_POSTGRESQL_H_ +#include "include/bareos.h" +#include "dird/dbcopy/database_export.h" + class DatabaseConnection; class DatabaseTableDescriptions; class DatabaseExportPostgresql : public DatabaseExport { public: DatabaseExportPostgresql(const DatabaseConnection& db_connection, + InsertMode mode = InsertMode::kSqlCopy, bool clear_tables = false); ~DatabaseExportPostgresql(); @@ -52,11 +56,14 @@ class DatabaseExportPostgresql : public DatabaseExport { private: bool transaction_open_{false}; bool cursor_start_new_table_{false}; + InsertMode insert_mode_{}; SequenceSchemaVector sequence_schema_vector_; void SelectSequenceSchema(); void CursorStartTable(const std::string& table_name); - bool TableExists(const std::string& table_name); + bool TableExists(const std::string& table_name, + std::vector& column_names); + bool UseCopyInsertion(); static int ResultHandlerSequenceSchema(void* ctx, int fields, char** row); static int ResultHandlerCompare(void* ctx, int fields, char** row); }; diff --git a/core/src/dird/dbcopy/database_import_mysql.cc b/core/src/dird/dbcopy/database_import_mysql.cc index 79cedefe8a2..bb83e090e46 100644 --- a/core/src/dird/dbcopy/database_import_mysql.cc +++ b/core/src/dird/dbcopy/database_import_mysql.cc @@ -35,12 +35,30 @@ #include #include + +static void no_conversion(BareosDb* db, ColumnData& fd) +{ + fd.data_pointer = fd.data_pointer ? fd.data_pointer : ""; +} + +static const ColumnDescription::DataTypeConverterMap + db_import_converter_map_mysql{ + {"bigint", no_conversion}, {"binary", no_conversion}, + {"blob", no_conversion}, {"char", no_conversion}, + {"datetime", no_conversion}, {"decimal", no_conversion}, + {"enum", no_conversion}, {"int", no_conversion}, + {"longblob", no_conversion}, {"smallint", no_conversion}, + {"text", no_conversion}, {"timestamp", no_conversion}, + {"tinyblob", no_conversion}, {"tinyint", no_conversion}, + {"varchar", no_conversion}}; + DatabaseImportMysql::DatabaseImportMysql( const DatabaseConnection& db_connection, std::size_t maximum_amount_of_rows_in) : DatabaseImport(db_connection) , limit_amount_of_rows_(maximum_amount_of_rows_in) { + table_descriptions_->SetAllConverterCallbacks(db_import_converter_map_mysql); } struct ResultHandlerContext { @@ -147,7 +165,7 @@ static void ReadoutSizeOfRestoreObject(ResultHandlerContext* r, int field_index_longblob, char** row) { - auto invalid = std::numeric_limits::max(); + auto constexpr invalid = std::numeric_limits::max(); std::size_t index_of_restore_object = invalid; for (std::size_t i = 0; i < r->column_descriptions.size(); i++) { @@ -190,7 +208,7 @@ void DatabaseImportMysql::FillRowWithDatabaseResult(ResultHandlerContext* r, for (std::size_t i = 0; i < r->column_descriptions.size(); i++) { row_data.columns[i].data_pointer = row[i]; - r->column_descriptions[i]->db_import_converter(r->db, row_data.columns[i]); + r->column_descriptions[i]->converter(r->db, row_data.columns[i]); } } diff --git a/core/src/dird/dbcopy/database_table_descriptions.cc b/core/src/dird/dbcopy/database_table_descriptions.cc index 094f80d6ab4..7289f64d843 100644 --- a/core/src/dird/dbcopy/database_table_descriptions.cc +++ b/core/src/dird/dbcopy/database_table_descriptions.cc @@ -124,6 +124,16 @@ const ColumnDescription* DatabaseTableDescriptions::GetColumnDescription( return (c == table->column_descriptions.cend()) ? nullptr : c->get(); } +void DatabaseTableDescriptions::SetAllConverterCallbacks( + const ColumnDescription::DataTypeConverterMap& map) +{ + for (auto& table : tables) { + for (auto& column : table.column_descriptions) { + column->RegisterConverterCallbackFromMap(map); + } + } +} + std::unique_ptr DatabaseTableDescriptions::Create( const DatabaseConnection& connection) { diff --git a/core/src/dird/dbcopy/database_table_descriptions.h b/core/src/dird/dbcopy/database_table_descriptions.h index a6c56734754..a534a44fc8f 100644 --- a/core/src/dird/dbcopy/database_table_descriptions.h +++ b/core/src/dird/dbcopy/database_table_descriptions.h @@ -58,6 +58,8 @@ class DatabaseTableDescriptions { const std::string& table_name, const std::string& column_name) const; + void SetAllConverterCallbacks(const ColumnDescription::DataTypeConverterMap&); + virtual ~DatabaseTableDescriptions() = default; protected: diff --git a/core/src/dird/dbcopy/dbcopy.cc b/core/src/dird/dbcopy/dbcopy.cc index c15582198cb..ed45cb86c3c 100644 --- a/core/src/dird/dbcopy/dbcopy.cc +++ b/core/src/dird/dbcopy/dbcopy.cc @@ -72,7 +72,11 @@ class DbCopy { std::cout << "gathering information about destination catalog \"" << cl.destination_db_resource_name << "\"..." << std::endl; std::unique_ptr exp( - DatabaseExport::Create(*destination_db_, cl.empty_destination_tables)); + DatabaseExport::Create(*destination_db_, + cl.use_sql_insert_statements_instead_of_copy + ? DatabaseExport::InsertMode::kSqlInsert + : DatabaseExport::InsertMode::kSqlCopy, + cl.empty_destination_tables)); std::cout << "copying tables..." << std::endl; imp->ExportTo(*exp); @@ -144,12 +148,16 @@ class DbCopy { bool options_error{false}; int argument_count{}; - while ((c = getopt(argc, argv, "c:l:?")) != -1 && !options_error) { + while ((c = getopt(argc, argv, "ic:l:?")) != -1 && !options_error) { switch (c) { case 'c': configpath_ = optarg; argument_count += 2; break; + case 'i': + use_sql_insert_statements_instead_of_copy = true; + argument_count += 1; + break; #if 0 case 'd': empty_destination_tables = true; @@ -189,6 +197,7 @@ class DbCopy { std::string configpath_{"/etc/bareos"}; std::string source_db_resource_name, destination_db_resource_name; bool empty_destination_tables{false}; + bool use_sql_insert_statements_instead_of_copy{false}; #if 0 bool compare_all_rows{false}; #endif @@ -198,12 +207,14 @@ class DbCopy { { kBareosVersionStrings.PrintCopyright(stderr, 2020); - fprintf(stderr, - _("Usage: bareos-dbcopy [options] Source Destination\n" - " -c use as configuration file or " - "directory\n" - " -? print this message.\n" - "\n")); + fprintf( + stderr, + _("Usage: bareos-dbcopy [options] Source Destination\n" + " -c use as configuration file or " + "directory\n" + " -i use SQL INSERT statements instead of COPY\n" + " -? print this message.\n" + "\n")); } }; // class CommandLineParser