summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/dbTypes.h5
-rw-r--r--lib/output/pq/typeMapper.cpp2
-rw-r--r--test/test-e2e.cpp156
3 files changed, 158 insertions, 5 deletions
diff --git a/lib/dbTypes.h b/lib/dbTypes.h
index a9ed029..8cac3ca 100644
--- a/lib/dbTypes.h
+++ b/lib/dbTypes.h
@@ -164,14 +164,11 @@ namespace MyGrate {
else if constexpr (std::is_floating_point_v<R>) {
return visit(detail::SafeExtract<R, std::is_floating_point> {});
}
- else if constexpr (std::is_same_v<std::string_view, R>) {
- return get<std::string_view>();
- }
else if constexpr (std::is_same_v<std::string, R>) {
return visit(detail::ToString {});
}
else {
- static_assert(detail::is_false<R>::value, "Cannot extract one of these");
+ return get<R>();
}
}
};
diff --git a/lib/output/pq/typeMapper.cpp b/lib/output/pq/typeMapper.cpp
index 02b0a4f..e7a4eb1 100644
--- a/lib/output/pq/typeMapper.cpp
+++ b/lib/output/pq/typeMapper.cpp
@@ -42,7 +42,7 @@ namespace MyGrate::Output::Pq {
add(R"(timestamp(\(\d+\))?)"_r, "timestamp without time zone"); // drops precision
add(R"(time)"_r, "time without time zone");
add(R"(time(\(\d+\))?)"_r, "time without time zone"); // drops precision
- add(R"(year)"_r, "smallint");
+ add(R"(year(\(\d+\))?)"_r, "smallint");
// https://dev.mysql.com/doc/refman/8.0/en/string-types.html
add(R"((var)?binary(\(\d+\))?)"_r, "bytea");
add(R"((var)?char(\(\d+\))?)"_r, "");
diff --git a/test/test-e2e.cpp b/test/test-e2e.cpp
index d0d580c..67436ab 100644
--- a/test/test-e2e.cpp
+++ b/test/test-e2e.cpp
@@ -19,6 +19,7 @@
#include <sql/simpleSelectAll.h>
#include <sql/simpleUpdate.h>
#include <sql/simpleUpdateAll.h>
+#include <streamSupport.h>
#include <thread>
class TestUpdateDatabase : public MyGrate::Output::Pq::UpdateDatabase {
@@ -198,4 +199,159 @@ BOOST_AUTO_TEST_CASE(txns, *boost::unit_test::timeout(15))
BOOST_CHECK_EQUAL(recs->at(3, 1).get<std::string_view>(), "Same");
}
+template<int T> struct TypeTestDetail;
+#define TEST_TYPE(MYSQL_TYPE, IN, OUT, MYTYPE) \
+ template<> struct TypeTestDetail<MYSQL_TYPE> { \
+ using OutType = OUT; \
+ static constexpr auto mytype = #MYTYPE; \
+ static IN generate(size_t n); \
+ }; \
+ IN TypeTestDetail<MYSQL_TYPE>::generate(size_t n)
+
+TEST_TYPE(MYSQL_TYPE_STRING, std::string, std::string_view, text)
+{
+ return std::string(n * 200, 'f');
+}
+TEST_TYPE(MYSQL_TYPE_VARCHAR, std::string, std::string_view, varchar(2048))
+{
+ return std::string(n * 20, 'f');
+}
+TEST_TYPE(MYSQL_TYPE_JSON, std::string, std::string_view, json)
+{
+ return MyGrate::scprintf<"{ json: %? }">(n);
+}
+TEST_TYPE(MYSQL_TYPE_ENUM, std::string_view, std::string_view, enum('alpha', 'beta', 'gamma'))
+{
+ static constexpr std::array<std::string_view, 3> vals {"alpha", "beta", "gamma"};
+ return vals[n % vals.size()];
+}
+TEST_TYPE(MYSQL_TYPE_TINY, int8_t, int8_t, tinyint)
+{
+ return (int8_t)n;
+}
+TEST_TYPE(MYSQL_TYPE_SHORT, int16_t, int16_t, smallint)
+{
+ return (int16_t)n;
+}
+TEST_TYPE(MYSQL_TYPE_INT24, int32_t, int32_t, int)
+{
+ return (int32_t)n;
+}
+TEST_TYPE(MYSQL_TYPE_YEAR, int16_t, int16_t, year)
+{
+ if (!n) {
+ return 0;
+ }
+ return (int16_t)n + 1901;
+}
+TEST_TYPE(MYSQL_TYPE_LONG, int32_t, int32_t, int)
+{
+ return (int32_t)n;
+}
+TEST_TYPE(MYSQL_TYPE_LONGLONG, int64_t, int64_t, bigint)
+{
+ return (int64_t)n;
+}
+TEST_TYPE(MYSQL_TYPE_FLOAT, float, float, float)
+{
+ return (float)n;
+}
+TEST_TYPE(MYSQL_TYPE_DOUBLE, double, double, real)
+{
+ return (double)n;
+}
+TEST_TYPE(MYSQL_TYPE_DATETIME, MyGrate::DateTime, MyGrate::DateTime, datetime)
+{
+ time_t t = time(nullptr);
+ t -= (n * 12345679);
+ struct tm tm {
+ };
+ gmtime_r(&t, &tm);
+ return MyGrate::DateTime {tm};
+}
+TEST_TYPE(MYSQL_TYPE_DATE, MyGrate::Date, MyGrate::Date, date)
+{
+ time_t t = time(nullptr);
+ t -= (n * 12345679);
+ struct tm tm {
+ };
+ gmtime_r(&t, &tm);
+ return MyGrate::Date {tm};
+}
+TEST_TYPE(MYSQL_TYPE_TIME, MyGrate::Time, MyGrate::Time, time)
+{
+ time_t t = time(nullptr);
+ t -= (n * 12345679);
+ struct tm tm {
+ };
+ gmtime_r(&t, &tm);
+ auto r = MyGrate::Time {tm};
+ std::cerr << r << "\n";
+ return r;
+}
+
+template<int MYSQL_TYPE, typename Test>
+void
+replication_data_type_impl(Test * test)
+{
+ using T = TypeTestDetail<MYSQL_TYPE>;
+ using I = decltype(T::generate(0));
+ using O = typename T::OutType;
+
+ constexpr auto ROWS {100U};
+ BOOST_TEST_INFO(T::mytype);
+ test->mym.query(
+ MyGrate::scprintf<"CREATE TABLE test(id INT AUTO_INCREMENT, val %?, PRIMARY KEY(id))">(T::mytype).c_str());
+ TestUpdateDatabase & out {test->getUpdateDatabase()};
+ out.addTable(&test->mym, "test");
+
+ std::vector<I> vals;
+ vals.reserve(ROWS);
+
+ // insert test records
+ test->run();
+ MyGrate::Tx {&test->mym}([&vals, test] {
+ MyGrate::DbStmt<"INSERT INTO test(val) VALUES(?)"> ins;
+ for (size_t n {}; n < ROWS; n++) {
+ vals.push_back(T::generate(n));
+ ins.execute(&test->mym, vals.back());
+ }
+ });
+ test->stopAfter(EventCounterTarget {}.add(WRITE_ROWS_EVENT_V1, ROWS));
+
+ // read test records
+ auto rs {MyGrate::DbStmt<"SELECT val FROM testout.test ORDER BY id">::execute(&test->pqm)};
+ std::vector<O> outs;
+ vals.reserve(ROWS);
+ for (auto v : *rs) {
+ outs.push_back(v[0]);
+ }
+
+ // Check values
+ BOOST_CHECK_EQUAL_COLLECTIONS(vals.begin(), vals.end(), outs.begin(), outs.end());
+}
+
+#define TEST_MYSQL_TYPE(T) \
+ BOOST_TEST_DECORATOR(*boost::unit_test::timeout(5)) \
+ BOOST_AUTO_TEST_CASE(replication_data_type_##T) \
+ { \
+ replication_data_type_impl<T>(this); \
+ }
+
+TEST_MYSQL_TYPE(MYSQL_TYPE_STRING);
+TEST_MYSQL_TYPE(MYSQL_TYPE_VARCHAR);
+TEST_MYSQL_TYPE(MYSQL_TYPE_JSON);
+// TEST_MYSQL_TYPE(MYSQL_TYPE_ENUM); // we don't have sufficient data available to support this
+TEST_MYSQL_TYPE(MYSQL_TYPE_TINY);
+TEST_MYSQL_TYPE(MYSQL_TYPE_SHORT);
+TEST_MYSQL_TYPE(MYSQL_TYPE_LONG);
+TEST_MYSQL_TYPE(MYSQL_TYPE_INT24);
+TEST_MYSQL_TYPE(MYSQL_TYPE_LONGLONG);
+TEST_MYSQL_TYPE(MYSQL_TYPE_FLOAT);
+TEST_MYSQL_TYPE(MYSQL_TYPE_DOUBLE);
+TEST_MYSQL_TYPE(MYSQL_TYPE_YEAR);
+TEST_MYSQL_TYPE(MYSQL_TYPE_DATETIME);
+TEST_MYSQL_TYPE(MYSQL_TYPE_DATE);
+TEST_MYSQL_TYPE(MYSQL_TYPE_TIME);
+
BOOST_AUTO_TEST_SUITE_END();