Skip to content

Commit c3f9192

Browse files
authored
Add RawParameter API to pass raw SQL parameters to the database directly (#2335)
1 parent e46e05e commit c3f9192

File tree

3 files changed

+200
-20
lines changed

3 files changed

+200
-20
lines changed

orm_lib/inc/drogon/orm/SqlBinder.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ enum class Mode
116116
Blocking
117117
};
118118

119+
struct RawParameter
120+
{
121+
std::shared_ptr<void> obj;
122+
const char *parameter;
123+
int length;
124+
int format;
125+
};
126+
119127
namespace internal
120128
{
121129
template <typename T>
@@ -434,6 +442,15 @@ class DROGON_EXPORT SqlBinder : public trantor::NonCopyable
434442
return *this;
435443
}
436444

445+
self &operator<<(const RawParameter &);
446+
447+
self &operator<<(RawParameter &param)
448+
{
449+
return operator<<((const RawParameter &)param);
450+
}
451+
452+
self &operator<<(RawParameter &&);
453+
437454
// template <>
438455
self &operator<<(const char str[])
439456
{

orm_lib/src/SqlBinder.cc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,26 @@ SqlBinder::~SqlBinder()
137137
}
138138
}
139139

140+
SqlBinder &SqlBinder::operator<<(const RawParameter &param)
141+
{
142+
objs_.push_back(param.obj);
143+
parameters_.push_back(param.parameter);
144+
lengths_.push_back(param.length);
145+
formats_.push_back(param.format);
146+
++parametersNumber_;
147+
return *this;
148+
}
149+
150+
SqlBinder &SqlBinder::operator<<(RawParameter &&param)
151+
{
152+
objs_.push_back(std::move(param.obj));
153+
parameters_.push_back(std::move(param.parameter));
154+
lengths_.push_back(std::move(param.length));
155+
formats_.push_back(std::move(param.format));
156+
++parametersNumber_;
157+
return *this;
158+
}
159+
140160
SqlBinder &SqlBinder::operator<<(const std::string_view &str)
141161
{
142162
auto obj = std::make_shared<std::string>(str.data(), str.length());

orm_lib/tests/db_test.cc

Lines changed: 163 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,24 @@ DROGON_TEST(PostgreTest)
287287
FAULT("postgresql - DbClient streaming-type interface(8) what():",
288288
e.base().what());
289289
};
290-
/// 1.10 clean up
290+
/// 1.10 query with raw parameter
291+
auto rawParamData = std::make_shared<int>(htonl(3));
292+
auto rawParam = RawParameter{rawParamData,
293+
reinterpret_cast<char *>(rawParamData.get()),
294+
sizeof(int),
295+
1};
296+
*clientPtr << "select * from users where length(user_id)=$1" << rawParam >>
297+
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>
298+
[TEST_CTX](const DrogonDbException &e) {
299+
FAULT("postgresql - DbClient streaming-type interface(9) what():",
300+
e.base().what());
301+
};
302+
303+
/// 1.11 clean up
291304
*clientPtr << "truncate table users restart identity" >>
292305
[TEST_CTX](const Result &r) { SUCCESS(); } >>
293306
[TEST_CTX](const DrogonDbException &e) {
294-
FAULT("postgresql - DbClient streaming-type interface(9) what():",
307+
FAULT("postgresql - DbClient streaming-type interface(10) what():",
295308
e.base().what());
296309
};
297310
/// Test asynchronous method
@@ -379,12 +392,21 @@ DROGON_TEST(PostgreTest)
379392
"postgresql1",
380393
"pg",
381394
"postgresql");
382-
/// 2.6 clean up
395+
/// 2.6 query with raw parameter
396+
clientPtr->execSqlAsync(
397+
"select * from users where length(user_id)=$1",
398+
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },
399+
[TEST_CTX](const DrogonDbException &e) {
400+
FAULT("postgresql - DbClient asynchronous interface(7) what():",
401+
e.base().what());
402+
},
403+
rawParam);
404+
/// 2.7 clean up
383405
clientPtr->execSqlAsync(
384406
"truncate table users restart identity",
385407
[TEST_CTX](const Result &r) { SUCCESS(); },
386408
[TEST_CTX](const DrogonDbException &e) {
387-
FAULT("postgresql - DbClient asynchronous interface(7) what():",
409+
FAULT("postgresql - DbClient asynchronous interface(8) what():",
388410
e.base().what());
389411
});
390412

@@ -464,7 +486,19 @@ DROGON_TEST(PostgreTest)
464486
{
465487
SUCCESS();
466488
}
467-
/// 3.6 clean up
489+
/// 3.6 query with raw parameter
490+
try
491+
{
492+
auto r = clientPtr->execSqlSync(
493+
"select * from users where length(user_id)=$1", rawParam);
494+
MANDATE(r.size() == 1);
495+
}
496+
catch (const DrogonDbException &e)
497+
{
498+
FAULT("postgresql - DbClient asynchronous interface(4) what():",
499+
e.base().what());
500+
}
501+
/// 3.7 clean up
468502
try
469503
{
470504
auto r =
@@ -557,7 +591,20 @@ DROGON_TEST(PostgreTest)
557591
{
558592
SUCCESS();
559593
}
560-
/// 4.6 clean up
594+
/// 4.6 query with raw parameter
595+
f = clientPtr->execSqlAsyncFuture(
596+
"select * from users where length(user_id)=$1", rawParam);
597+
try
598+
{
599+
auto r = f.get();
600+
MANDATE(r.size() == 1);
601+
}
602+
catch (const DrogonDbException &e)
603+
{
604+
FAULT("postgresql - DbClient future interface(4) what():",
605+
e.base().what());
606+
}
607+
/// 4.7 clean up
561608
f = clientPtr->execSqlAsyncFuture("truncate table users restart identity");
562609
try
563610
{
@@ -1670,11 +1717,28 @@ DROGON_TEST(MySQLTest)
16701717
FAULT("mysql - DbClient streaming-type interface(8) what():",
16711718
e.base().what());
16721719
};
1673-
/// 1.10 truncate
1720+
/// 1.10 query with raw parameter
1721+
// MariaDB uses little-endian, so the opposite of network ordering :P
1722+
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1723+
auto rawParamData = std::make_shared<int>(3);
1724+
#else
1725+
auto rawParamData = std::make_shared<int>(0x03000000); // byteswapped 3
1726+
#endif
1727+
auto rawParam = RawParameter{rawParamData,
1728+
reinterpret_cast<char *>(rawParamData.get()),
1729+
sizeof(int),
1730+
internal::MySqlLong};
1731+
*clientPtr << "select * from users where length(user_id)=?" << rawParam >>
1732+
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>
1733+
[TEST_CTX](const DrogonDbException &e) {
1734+
FAULT("mysql - DbClient streaming-type interface(9) what():",
1735+
e.base().what());
1736+
};
1737+
/// 1.11 truncate
16741738
*clientPtr << "truncate table users" >> [TEST_CTX](const Result &r) {
16751739
SUCCESS();
16761740
} >> [TEST_CTX](const DrogonDbException &e) {
1677-
FAULT("mysql - DbClient streaming-type interface(9) what():",
1741+
FAULT("mysql - DbClient streaming-type interface(10) what():",
16781742
e.base().what());
16791743
};
16801744
/// Test asynchronous method
@@ -1759,12 +1823,21 @@ DROGON_TEST(MySQLTest)
17591823
"postgresql1",
17601824
"pg",
17611825
"postgresql");
1762-
/// 2.6 truncate
1826+
/// 2.6 query with raw parameter
1827+
clientPtr->execSqlAsync(
1828+
"select * from users where length(user_id)=?",
1829+
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },
1830+
[TEST_CTX](const DrogonDbException &e) {
1831+
FAULT("mysql - DbClient asynchronous interface(7) what():",
1832+
e.base().what());
1833+
},
1834+
rawParam);
1835+
/// 2.7 truncate
17631836
clientPtr->execSqlAsync(
17641837
"truncate table users",
17651838
[TEST_CTX](const Result &r) { SUCCESS(); },
17661839
[TEST_CTX](const DrogonDbException &e) {
1767-
FAULT("mysql - DbClient asynchronous interface(7) what():",
1840+
FAULT("mysql - DbClient asynchronous interface(9) what():",
17681841
e.base().what());
17691842
});
17701843

@@ -1845,7 +1918,19 @@ DROGON_TEST(MySQLTest)
18451918
{
18461919
SUCCESS();
18471920
}
1848-
/// 3.6 truncate
1921+
/// 3.6 query with raw parameter
1922+
try
1923+
{
1924+
auto r = clientPtr->execSqlSync(
1925+
"select * from users where length(user_id)=?", rawParam);
1926+
MANDATE(r.size() == 1);
1927+
}
1928+
catch (const DrogonDbException &e)
1929+
{
1930+
FAULT("mysql - DbClient asynchronous interface(4) what():",
1931+
e.base().what());
1932+
}
1933+
/// 3.7 truncate
18491934
try
18501935
{
18511936
auto r = clientPtr->execSqlSync("truncate table users");
@@ -1932,7 +2017,19 @@ DROGON_TEST(MySQLTest)
19322017
{
19332018
SUCCESS();
19342019
}
1935-
/// 4.6 truncate
2020+
/// 4.6. query with raw parameter
2021+
f = clientPtr->execSqlAsyncFuture(
2022+
"select * from users where length(user_id)=?", rawParam);
2023+
try
2024+
{
2025+
auto r = f.get();
2026+
MANDATE(r.size() == 1);
2027+
}
2028+
catch (const DrogonDbException &e)
2029+
{
2030+
FAULT("mysql - DbClient future interface(5) what():", e.base().what());
2031+
}
2032+
/// 4.7 truncate
19362033
f = clientPtr->execSqlAsyncFuture("truncate table users");
19372034
try
19382035
{
@@ -1941,7 +2038,7 @@ DROGON_TEST(MySQLTest)
19412038
}
19422039
catch (const DrogonDbException &e)
19432040
{
1944-
FAULT("mysql - DbClient future interface(5) what():", e.base().what());
2041+
FAULT("mysql - DbClient future interface(6) what():", e.base().what());
19452042
}
19462043

19472044
/// 5 Test Result and Row exception throwing
@@ -2881,17 +2978,29 @@ DROGON_TEST(SQLite3Test)
28812978
FAULT("sqlite3 - DbClient streaming-type interface(8) what():",
28822979
e.base().what());
28832980
};
2884-
/// 1.10 clean up
2981+
/// 1.10 query with raw parameter
2982+
auto rawParamData = std::make_shared<int>(3);
2983+
auto rawParam = RawParameter{rawParamData,
2984+
reinterpret_cast<char *>(rawParamData.get()),
2985+
0,
2986+
Sqlite3TypeInt};
2987+
*clientPtr << "select * from users where length(user_id) = ?" << rawParam >>
2988+
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>
2989+
[TEST_CTX](const DrogonDbException &e) {
2990+
FAULT("sqlite3 - DbClient streaming-type interface(9) what():",
2991+
e.base().what());
2992+
};
2993+
/// 1.11 clean up
28852994
*clientPtr << "delete from users" >> [TEST_CTX](const Result &r) {
28862995
SUCCESS();
28872996
} >> [TEST_CTX](const DrogonDbException &e) {
2888-
FAULT("sqlite3 - DbClient streaming-type interface(9.1) what():",
2997+
FAULT("sqlite3 - DbClient streaming-type interface(10.1) what():",
28892998
e.base().what());
28902999
};
28913000
*clientPtr << "UPDATE sqlite_sequence SET seq = 0" >>
28923001
[TEST_CTX](const Result &r) { SUCCESS(); } >>
28933002
[TEST_CTX](const DrogonDbException &e) {
2894-
FAULT("sqlite3 - DbClient streaming-type interface(9.2) what():",
3003+
FAULT("sqlite3 - DbClient streaming-type interface(10.2) what():",
28953004
e.base().what());
28963005
};
28973006
/// Test asynchronous method
@@ -2975,19 +3084,28 @@ DROGON_TEST(SQLite3Test)
29753084
"postgresql1",
29763085
"pg",
29773086
"postgresql");
2978-
/// 2.6 clean up
3087+
/// 2.6 query with raw parameter
3088+
clientPtr->execSqlAsync(
3089+
"select * from users where length(user_id) = ?",
3090+
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },
3091+
[TEST_CTX](const DrogonDbException &e) {
3092+
FAULT("sqlite3 - DbClient asynchronous interface(7) what():",
3093+
e.base().what());
3094+
},
3095+
rawParam);
3096+
/// 2.7 clean up
29793097
clientPtr->execSqlAsync(
29803098
"delete from users",
29813099
[TEST_CTX](const Result &r) { SUCCESS(); },
29823100
[TEST_CTX](const DrogonDbException &e) {
2983-
FAULT("sqlite3 - DbClient asynchronous interface(7.1) what():",
3101+
FAULT("sqlite3 - DbClient asynchronous interface(8.1) what():",
29843102
e.base().what());
29853103
});
29863104
clientPtr->execSqlAsync(
29873105
"UPDATE sqlite_sequence SET seq = 0",
29883106
[TEST_CTX](const Result &r) { SUCCESS(); },
29893107
[TEST_CTX](const DrogonDbException &e) {
2990-
FAULT("sqlite3 - DbClient asynchronous interface(7.2) what():",
3108+
FAULT("sqlite3 - DbClient asynchronous interface(8.2) what():",
29913109
e.base().what());
29923110
});
29933111

@@ -3074,7 +3192,19 @@ DROGON_TEST(SQLite3Test)
30743192
{
30753193
SUCCESS();
30763194
}
3077-
/// 3.6 clean up
3195+
/// 3.6 query with raw parameter
3196+
try
3197+
{
3198+
auto r = clientPtr->execSqlSync(
3199+
"select * from users where length(user_id) = ?", rawParam);
3200+
MANDATE(r.size() == 1);
3201+
}
3202+
catch (const DrogonDbException &e)
3203+
{
3204+
FAULT("sqlite3 - DbClient asynchronous interface(4) what():",
3205+
e.base().what());
3206+
}
3207+
/// 3.7 clean up
30783208
try
30793209
{
30803210
auto r = clientPtr->execSqlSync("delete from users");
@@ -3177,6 +3307,19 @@ DROGON_TEST(SQLite3Test)
31773307
{
31783308
SUCCESS();
31793309
}
3310+
/// 4.6 query with raw parameter
3311+
f = clientPtr->execSqlAsyncFuture(
3312+
"select * from users where length(user_id)=?", rawParam);
3313+
try
3314+
{
3315+
auto r = f.get();
3316+
MANDATE(r.size() == 1);
3317+
}
3318+
catch (const DrogonDbException &e)
3319+
{
3320+
FAULT("sqlite3 - DbClient future interface(4) what():",
3321+
e.base().what());
3322+
}
31803323
/// 4.6 clean up
31813324
f = clientPtr->execSqlAsyncFuture("delete from users");
31823325
try

0 commit comments

Comments
 (0)