From aa5d32e30c6ea2c2c2687ab6533d476963039dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Tue, 23 Jan 2024 16:09:59 +0100 Subject: [PATCH] blob progress --- .../include/postgres_parameter_binder.h | 2 +- backends/postgres/src/postgres_dialect.cpp | 3 +++ .../src/postgres_parameter_binder.cpp | 2 +- include/matador/sql/dialect.hpp | 4 +++ include/matador/utils/string.hpp | 3 +++ src/sql/query_builder.cpp | 10 +++++--- src/utils/string.cpp | 15 +++++++++++ test/QueryBuilderTest.cpp | 25 +++++++++++++++++++ 8 files changed, 58 insertions(+), 6 deletions(-) diff --git a/backends/postgres/include/postgres_parameter_binder.h b/backends/postgres/include/postgres_parameter_binder.h index 6f349ff..fefebb7 100644 --- a/backends/postgres/include/postgres_parameter_binder.h +++ b/backends/postgres/include/postgres_parameter_binder.h @@ -32,7 +32,7 @@ public: void bind(size_t pos, const utils::blob &blob) override; - const std::vector& params() const; + [[nodiscard]] const std::vector& params() const; private: std::vector strings_; diff --git a/backends/postgres/src/postgres_dialect.cpp b/backends/postgres/src/postgres_dialect.cpp index d28ba05..4520e71 100644 --- a/backends/postgres/src/postgres_dialect.cpp +++ b/backends/postgres/src/postgres_dialect.cpp @@ -10,6 +10,9 @@ .with_placeholder_func([](size_t index) { return "$" + std::to_string(index); }) + .with_token_replace_map({ + {dialect::token_t::BEGIN_BINARY_DATA, "E'\\\\"} + }) .with_data_type_replace_map({ {data_type_t::type_blob, "BYTEA"} }) diff --git a/backends/postgres/src/postgres_parameter_binder.cpp b/backends/postgres/src/postgres_parameter_binder.cpp index f62a213..610cc01 100644 --- a/backends/postgres/src/postgres_parameter_binder.cpp +++ b/backends/postgres/src/postgres_parameter_binder.cpp @@ -136,7 +136,7 @@ void postgres_parameter_binder::bind(size_t pos, const std::string &str, size_t void postgres_parameter_binder::bind(size_t pos, const utils::blob &blob) { - + params_[pos] = ""; } const std::vector &postgres_parameter_binder::params() const diff --git a/include/matador/sql/dialect.hpp b/include/matador/sql/dialect.hpp index 69917f7..a69e780 100644 --- a/include/matador/sql/dialect.hpp +++ b/include/matador/sql/dialect.hpp @@ -54,6 +54,8 @@ public: START_QUOTE, END_QUOTE, STRING_QUOTE, + BEGIN_BINARY_DATA, + END_BINARY_DATA, NONE }; @@ -181,6 +183,8 @@ private: {token_t::START_QUOTE, "\""}, {token_t::END_QUOTE, "\""}, {token_t::STRING_QUOTE, "'"}, + {token_t::BEGIN_BINARY_DATA, "X'"}, + {token_t::END_BINARY_DATA, "'"}, {token_t::NONE, ""} }; diff --git a/include/matador/utils/string.hpp b/include/matador/utils/string.hpp index ba1bb79..4d82448 100644 --- a/include/matador/utils/string.hpp +++ b/include/matador/utils/string.hpp @@ -1,6 +1,8 @@ #ifndef QUERY_STRING_HPP #define QUERY_STRING_HPP +#include "matador/utils/types.hpp" + #include #include @@ -29,6 +31,7 @@ size_t split(const std::string &str, char delim, std::vector &value void replace_all(std::string &in, const std::string &from, const std::string &to); const std::string& to_string(const std::string &str); +std::string to_string(const blob &data); /** * Joins a range of elements as string within a list diff --git a/src/sql/query_builder.cpp b/src/sql/query_builder.cpp index 5c43fa8..65bc2f0 100644 --- a/src/sql/query_builder.cpp +++ b/src/sql/query_builder.cpp @@ -24,7 +24,12 @@ void any_type_to_string_visitor::to_string(std::string &val) void any_type_to_string_visitor::to_string(utils::blob &val) { - + // "This is a binary Data string" as binary data: + // MySQL: X'5468697320697320612062616E617279204461746120737472696E67' + // Postgres: E'\\x5468697320697320612062616E617279204461746120737472696E67' + // MSSQL: 0x5468697320697320612062616E617279204461746120737472696E67 + // Sqlite: X'5468697320697320612062616E617279204461746120737472696E67' + result = d.token_at(dialect::token_t::BEGIN_BINARY_DATA) + utils::to_string(val) + d.token_at(dialect::token_t::END_BINARY_DATA); } void any_type_to_string_visitor::to_string(placeholder &/*val*/) @@ -292,20 +297,17 @@ query_builder &query_builder::values(const std::vector &values) std::string result{"("}; if (values.size() < 2) { for (auto val: values) { -// query_.result_vars.push_back(val); std::visit(value_to_string_, val); result.append(value_to_string_.result); } } else { auto it = values.begin(); auto val = *it++; -// query_.result_vars.push_back(val); std::visit(value_to_string_, val); result.append(value_to_string_.result); for (; it != values.end(); ++it) { result.append(", "); val = *it; -// query_.result_vars.push_back(val); std::visit(value_to_string_, val); result.append(value_to_string_.result); } diff --git a/src/utils/string.cpp b/src/utils/string.cpp index 1345c13..b3a3fd0 100644 --- a/src/utils/string.cpp +++ b/src/utils/string.cpp @@ -21,6 +21,21 @@ const std::string &to_string(const std::string &str) return str; } +std::string to_string(const blob &data) +{ + static constexpr char HEXITS[] = "0123456789ABCDEF"; + + std::string str(2 * data.size(), '\0'); + auto item = str.begin(); + + for(auto c : data) { + *item++ = HEXITS[c >> 4]; + *item++ = HEXITS[c & 0x0F]; + } + + return str; +} + size_t split(const std::string &str, char delim, std::vector &values) { std::stringstream ss(str); diff --git a/test/QueryBuilderTest.cpp b/test/QueryBuilderTest.cpp index 8160b66..4b3158c 100644 --- a/test/QueryBuilderTest.cpp +++ b/test/QueryBuilderTest.cpp @@ -149,4 +149,29 @@ TEST_CASE("Select sql statement string with offset and limit", "[query]") { REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person" OFFSET 10 LIMIT 20)"); REQUIRE(q.table_name == "person"); +} + +TEST_CASE("Create, insert and select a blob column", "[query][blob]") { + dialect d = dialect_builder::builder().create().build(); + query_builder query(d); + auto q = query.create().table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("data") + }).compile(); + + REQUIRE(q.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "data" BLOB NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##"); + REQUIRE(q.table_name == "person"); + + q = query.insert().into("person", { + "id", "name", "data" + }).values({7UL, "george", blob{1, 'A', 3, 4}}).compile(); + + REQUIRE(q.sql == R"(INSERT INTO "person" ("id", "name", "data") VALUES (7, 'george', X'01410304'))"); + REQUIRE(q.table_name == "person"); + + q = query.select({"id", "name", "data"}).from("person").compile(); + + REQUIRE(q.sql == R"(SELECT "id", "name", "data" FROM "person")"); + REQUIRE(q.table_name == "person"); } \ No newline at end of file