test_array.cxx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. #include <pqxx/transaction>
  2. #include "../test_helpers.hxx"
  3. // Test program for libpqxx array parsing.
  4. namespace pqxx
  5. {
  6. template<>
  7. struct nullness<array_parser::juncture> : no_null<array_parser::juncture>
  8. {};
  9. inline std::string to_string(pqxx::array_parser::juncture const &j)
  10. {
  11. using junc = pqxx::array_parser::juncture;
  12. switch (j)
  13. {
  14. case junc::row_start: return "row_start";
  15. case junc::row_end: return "row_end";
  16. case junc::null_value: return "null_value";
  17. case junc::string_value: return "string_value";
  18. case junc::done: return "done";
  19. default: return "UNKNOWN JUNCTURE: " + to_string(static_cast<int>(j));
  20. }
  21. }
  22. } // namespace pqxx
  23. namespace
  24. {
  25. void test_empty_arrays()
  26. {
  27. std::pair<pqxx::array_parser::juncture, std::string> output;
  28. // Parsing a null pointer just immediately returns "done".
  29. output = pqxx::array_parser(std::string_view()).get_next();
  30. PQXX_CHECK_EQUAL(
  31. output.first, pqxx::array_parser::juncture::done,
  32. "get_next on null array did not return done.");
  33. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  34. // Parsing an empty array string immediately returns "done".
  35. output = pqxx::array_parser("").get_next();
  36. PQXX_CHECK_EQUAL(
  37. output.first, pqxx::array_parser::juncture::done,
  38. "get_next on an empty array string did not return done.");
  39. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  40. // Parsing an empty array returns "row_start", "row_end", "done".
  41. pqxx::array_parser empty_parser("{}");
  42. output = empty_parser.get_next();
  43. PQXX_CHECK_EQUAL(
  44. output.first, pqxx::array_parser::juncture::row_start,
  45. "Empty array did not start with row_start.");
  46. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  47. output = empty_parser.get_next();
  48. PQXX_CHECK_EQUAL(
  49. output.first, pqxx::array_parser::juncture::row_end,
  50. "Empty array did not end with row_end.");
  51. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  52. output = empty_parser.get_next();
  53. PQXX_CHECK_EQUAL(
  54. output.first, pqxx::array_parser::juncture::done,
  55. "Empty array did not conclude with done.");
  56. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  57. }
  58. void test_null_value()
  59. {
  60. std::pair<pqxx::array_parser::juncture, std::string> output;
  61. pqxx::array_parser containing_null("{NULL}");
  62. output = containing_null.get_next();
  63. PQXX_CHECK_EQUAL(
  64. output.first, pqxx::array_parser::juncture::row_start,
  65. "Array containing null did not start with row_start.");
  66. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  67. output = containing_null.get_next();
  68. PQXX_CHECK_EQUAL(
  69. output.first, pqxx::array_parser::juncture::null_value,
  70. "Array containing null did not return null_value.");
  71. PQXX_CHECK_EQUAL(output.second, "", "Null value was not empty.");
  72. output = containing_null.get_next();
  73. PQXX_CHECK_EQUAL(
  74. output.first, pqxx::array_parser::juncture::row_end,
  75. "Array containing null did not end with row_end.");
  76. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  77. output = containing_null.get_next();
  78. PQXX_CHECK_EQUAL(
  79. output.first, pqxx::array_parser::juncture::done,
  80. "Array containing null did not conclude with done.");
  81. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  82. }
  83. void test_single_quoted_string()
  84. {
  85. std::pair<pqxx::array_parser::juncture, std::string> output;
  86. pqxx::array_parser parser("{'item'}");
  87. output = parser.get_next();
  88. PQXX_CHECK_EQUAL(
  89. output.first, pqxx::array_parser::juncture::row_start,
  90. "Array did not start with row_start.");
  91. output = parser.get_next();
  92. PQXX_CHECK_EQUAL(
  93. output.first, pqxx::array_parser::juncture::string_value,
  94. "Array did not return string_value.");
  95. PQXX_CHECK_EQUAL(output.second, "item", "Unexpected string value.");
  96. output = parser.get_next();
  97. PQXX_CHECK_EQUAL(
  98. output.first, pqxx::array_parser::juncture::row_end,
  99. "Array did not end with row_end.");
  100. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  101. output = parser.get_next();
  102. PQXX_CHECK_EQUAL(
  103. output.first, pqxx::array_parser::juncture::done,
  104. "Array did not conclude with done.");
  105. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  106. }
  107. void test_single_quoted_escaping()
  108. {
  109. std::pair<pqxx::array_parser::juncture, std::string> output;
  110. pqxx::array_parser parser("{'don''t\\\\ care'}");
  111. output = parser.get_next();
  112. PQXX_CHECK_EQUAL(
  113. output.first, pqxx::array_parser::juncture::row_start,
  114. "Array did not start with row_start.");
  115. output = parser.get_next();
  116. PQXX_CHECK_EQUAL(
  117. output.first, pqxx::array_parser::juncture::string_value,
  118. "Array did not return string_value.");
  119. PQXX_CHECK_EQUAL(output.second, "don't\\ care", "Unexpected string value.");
  120. output = parser.get_next();
  121. PQXX_CHECK_EQUAL(
  122. output.first, pqxx::array_parser::juncture::row_end,
  123. "Array did not end with row_end.");
  124. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  125. output = parser.get_next();
  126. PQXX_CHECK_EQUAL(
  127. output.first, pqxx::array_parser::juncture::done,
  128. "Array did not conclude with done.");
  129. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  130. }
  131. void test_double_quoted_string()
  132. {
  133. std::pair<pqxx::array_parser::juncture, std::string> output;
  134. pqxx::array_parser parser("{\"item\"}");
  135. output = parser.get_next();
  136. PQXX_CHECK_EQUAL(
  137. output.first, pqxx::array_parser::juncture::row_start,
  138. "Array did not start with row_start.");
  139. output = parser.get_next();
  140. PQXX_CHECK_EQUAL(
  141. output.first, pqxx::array_parser::juncture::string_value,
  142. "Array did not return string_value.");
  143. PQXX_CHECK_EQUAL(output.second, "item", "Unexpected string value.");
  144. output = parser.get_next();
  145. PQXX_CHECK_EQUAL(
  146. output.first, pqxx::array_parser::juncture::row_end,
  147. "Array did not end with row_end.");
  148. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  149. output = parser.get_next();
  150. PQXX_CHECK_EQUAL(
  151. output.first, pqxx::array_parser::juncture::done,
  152. "Array did not conclude with done.");
  153. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  154. }
  155. void test_double_quoted_escaping()
  156. {
  157. std::pair<pqxx::array_parser::juncture, std::string> output;
  158. pqxx::array_parser parser(R"--({"don''t\\ care"})--");
  159. output = parser.get_next();
  160. PQXX_CHECK_EQUAL(
  161. output.first, pqxx::array_parser::juncture::row_start,
  162. "Array did not start with row_start.");
  163. output = parser.get_next();
  164. PQXX_CHECK_EQUAL(
  165. output.first, pqxx::array_parser::juncture::string_value,
  166. "Array did not return string_value.");
  167. PQXX_CHECK_EQUAL(output.second, "don''t\\ care", "Unexpected string value.");
  168. output = parser.get_next();
  169. PQXX_CHECK_EQUAL(
  170. output.first, pqxx::array_parser::juncture::row_end,
  171. "Array did not end with row_end.");
  172. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  173. output = parser.get_next();
  174. PQXX_CHECK_EQUAL(
  175. output.first, pqxx::array_parser::juncture::done,
  176. "Array did not conclude with done.");
  177. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  178. }
  179. // A pair of double quotes in a double-quoted string is an escaped quote.
  180. void test_double_double_quoted_string()
  181. {
  182. std::pair<pqxx::array_parser::juncture, std::string> output;
  183. pqxx::array_parser parser{R"--({"3"" steel"})--"};
  184. output = parser.get_next();
  185. PQXX_CHECK_EQUAL(
  186. output.first, pqxx::array_parser::juncture::row_start,
  187. "Array did not start with row_start.");
  188. output = parser.get_next();
  189. PQXX_CHECK_EQUAL(
  190. output.first, pqxx::array_parser::juncture::string_value,
  191. "Array did not return string_value.");
  192. PQXX_CHECK_EQUAL(output.second, "3\" steel", "Unexpected string value.");
  193. }
  194. void test_unquoted_string()
  195. {
  196. std::pair<pqxx::array_parser::juncture, std::string> output;
  197. pqxx::array_parser parser("{item}");
  198. output = parser.get_next();
  199. PQXX_CHECK_EQUAL(
  200. output.first, pqxx::array_parser::juncture::row_start,
  201. "Array did not start with row_start.");
  202. output = parser.get_next();
  203. PQXX_CHECK_EQUAL(
  204. output.first, pqxx::array_parser::juncture::string_value,
  205. "Array did not return string_value.");
  206. PQXX_CHECK_EQUAL(output.second, "item", "Unexpected string value.");
  207. output = parser.get_next();
  208. PQXX_CHECK_EQUAL(
  209. output.first, pqxx::array_parser::juncture::row_end,
  210. "Array did not end with row_end.");
  211. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  212. output = parser.get_next();
  213. PQXX_CHECK_EQUAL(
  214. output.first, pqxx::array_parser::juncture::done,
  215. "Array did not conclude with done.");
  216. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  217. }
  218. void test_multiple_values()
  219. {
  220. std::pair<pqxx::array_parser::juncture, std::string> output;
  221. pqxx::array_parser parser("{1,2}");
  222. output = parser.get_next();
  223. PQXX_CHECK_EQUAL(
  224. output.first, pqxx::array_parser::juncture::row_start,
  225. "Array did not start with row_start.");
  226. output = parser.get_next();
  227. PQXX_CHECK_EQUAL(
  228. output.first, pqxx::array_parser::juncture::string_value,
  229. "Array did not return string_value.");
  230. PQXX_CHECK_EQUAL(output.second, "1", "Unexpected string value.");
  231. output = parser.get_next();
  232. PQXX_CHECK_EQUAL(
  233. output.first, pqxx::array_parser::juncture::string_value,
  234. "Array did not return string_value.");
  235. PQXX_CHECK_EQUAL(output.second, "2", "Unexpected string value.");
  236. output = parser.get_next();
  237. PQXX_CHECK_EQUAL(
  238. output.first, pqxx::array_parser::juncture::row_end,
  239. "Array did not end with row_end.");
  240. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  241. output = parser.get_next();
  242. PQXX_CHECK_EQUAL(
  243. output.first, pqxx::array_parser::juncture::done,
  244. "Array did not conclude with done.");
  245. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  246. }
  247. void test_nested_array()
  248. {
  249. std::pair<pqxx::array_parser::juncture, std::string> output;
  250. pqxx::array_parser parser("{{item}}");
  251. output = parser.get_next();
  252. PQXX_CHECK_EQUAL(
  253. output.first, pqxx::array_parser::juncture::row_start,
  254. "Array did not start with row_start.");
  255. output = parser.get_next();
  256. PQXX_CHECK_EQUAL(
  257. output.first, pqxx::array_parser::juncture::row_start,
  258. "Nested array did not start 2nd dimension with row_start.");
  259. output = parser.get_next();
  260. PQXX_CHECK_EQUAL(
  261. output.first, pqxx::array_parser::juncture::string_value,
  262. "Array did not return string_value.");
  263. PQXX_CHECK_EQUAL(output.second, "item", "Unexpected string value.");
  264. output = parser.get_next();
  265. PQXX_CHECK_EQUAL(
  266. output.first, pqxx::array_parser::juncture::row_end,
  267. "Nested array did not end 2nd dimension with row_end.");
  268. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  269. output = parser.get_next();
  270. PQXX_CHECK_EQUAL(
  271. output.first, pqxx::array_parser::juncture::row_end,
  272. "Array did not end with row_end.");
  273. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  274. output = parser.get_next();
  275. PQXX_CHECK_EQUAL(
  276. output.first, pqxx::array_parser::juncture::done,
  277. "Array did not conclude with done.");
  278. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  279. }
  280. void test_nested_array_with_multiple_entries()
  281. {
  282. std::pair<pqxx::array_parser::juncture, std::string> output;
  283. pqxx::array_parser parser("{{1,2},{3,4}}");
  284. output = parser.get_next();
  285. PQXX_CHECK_EQUAL(
  286. output.first, pqxx::array_parser::juncture::row_start,
  287. "Array did not start with row_start.");
  288. output = parser.get_next();
  289. PQXX_CHECK_EQUAL(
  290. output.first, pqxx::array_parser::juncture::row_start,
  291. "Nested array did not start 2nd dimension with row_start.");
  292. output = parser.get_next();
  293. PQXX_CHECK_EQUAL(
  294. output.first, pqxx::array_parser::juncture::string_value,
  295. "Array did not return string_value.");
  296. PQXX_CHECK_EQUAL(output.second, "1", "Unexpected string value.");
  297. output = parser.get_next();
  298. PQXX_CHECK_EQUAL(
  299. output.first, pqxx::array_parser::juncture::string_value,
  300. "Array did not return string_value.");
  301. PQXX_CHECK_EQUAL(output.second, "2", "Unexpected string value.");
  302. output = parser.get_next();
  303. PQXX_CHECK_EQUAL(
  304. output.first, pqxx::array_parser::juncture::row_end,
  305. "Nested array did not end 2nd dimension with row_end.");
  306. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  307. output = parser.get_next();
  308. PQXX_CHECK_EQUAL(
  309. output.first, pqxx::array_parser::juncture::row_start,
  310. "Nested array did not descend to 2nd dimension with row_start.");
  311. output = parser.get_next();
  312. PQXX_CHECK_EQUAL(
  313. output.first, pqxx::array_parser::juncture::string_value,
  314. "Array did not return string_value.");
  315. PQXX_CHECK_EQUAL(output.second, "3", "Unexpected string value.");
  316. output = parser.get_next();
  317. PQXX_CHECK_EQUAL(
  318. output.first, pqxx::array_parser::juncture::string_value,
  319. "Array did not return string_value.");
  320. PQXX_CHECK_EQUAL(output.second, "4", "Unexpected string value.");
  321. output = parser.get_next();
  322. PQXX_CHECK_EQUAL(
  323. output.first, pqxx::array_parser::juncture::row_end,
  324. "Nested array did not leave 2nd dimension with row_end.");
  325. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  326. output = parser.get_next();
  327. PQXX_CHECK_EQUAL(
  328. output.first, pqxx::array_parser::juncture::row_end,
  329. "Array did not end with row_end.");
  330. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  331. output = parser.get_next();
  332. PQXX_CHECK_EQUAL(
  333. output.first, pqxx::array_parser::juncture::done,
  334. "Array did not conclude with done.");
  335. PQXX_CHECK_EQUAL(output.second, "", "Unexpected nonempty output.");
  336. }
  337. void test_array_parse()
  338. {
  339. test_empty_arrays();
  340. test_null_value();
  341. test_single_quoted_string();
  342. test_single_quoted_escaping();
  343. test_double_quoted_string();
  344. test_double_quoted_escaping();
  345. test_double_double_quoted_string();
  346. test_unquoted_string();
  347. test_multiple_values();
  348. test_nested_array();
  349. test_nested_array_with_multiple_entries();
  350. }
  351. void test_generate_empty_array()
  352. {
  353. PQXX_CHECK_EQUAL(
  354. pqxx::to_string(std::vector<int>{}), "{}",
  355. "Basic array output is not as expected.");
  356. PQXX_CHECK_EQUAL(
  357. pqxx::to_string(std::vector<std::string>{}), "{}",
  358. "String array comes out different.");
  359. }
  360. void test_generate_null_value()
  361. {
  362. PQXX_CHECK_EQUAL(
  363. pqxx::to_string(std::vector<char const *>{nullptr}), "{NULL}",
  364. "Null array value did not come out as expected.");
  365. }
  366. void test_generate_single_item()
  367. {
  368. PQXX_CHECK_EQUAL(
  369. pqxx::to_string(std::vector<int>{42}), "{42}",
  370. "Numeric conversion came out wrong.");
  371. PQXX_CHECK_EQUAL(
  372. pqxx::to_string(std::vector<char const *>{"foo"}), "{\"foo\"}",
  373. "String array conversion came out wrong.");
  374. }
  375. void test_generate_multiple_items()
  376. {
  377. PQXX_CHECK_EQUAL(
  378. pqxx::to_string(std::vector<int>{5, 4, 3, 2}), "{5,4,3,2}",
  379. "Array with multiple values is not correct.");
  380. PQXX_CHECK_EQUAL(
  381. pqxx::to_string(std::vector<std::string>{"foo", "bar"}),
  382. "{\"foo\",\"bar\"}", "Array with multiple strings came out wrong.");
  383. }
  384. void test_generate_nested_array()
  385. {
  386. PQXX_CHECK_EQUAL(
  387. pqxx::to_string(std::vector<std::vector<int>>{{1, 2}, {3, 4}}),
  388. "{{1,2},{3,4}}", "Nested arrays don't work right.");
  389. }
  390. void test_generate_escaped_strings()
  391. {
  392. PQXX_CHECK_EQUAL(
  393. pqxx::to_string(std::vector<std::string>{"a\\b"}), "{\"a\\\\b\"}",
  394. "Backslashes are not escaped properly.");
  395. PQXX_CHECK_EQUAL(
  396. pqxx::to_string(std::vector<std::string>{"x\"y\""}), "{\"x\\\"y\\\"\"}",
  397. "Double quotes are not escaped properly.");
  398. }
  399. void test_array_generate()
  400. {
  401. test_generate_empty_array();
  402. test_generate_null_value();
  403. test_generate_single_item();
  404. test_generate_multiple_items();
  405. test_generate_nested_array();
  406. test_generate_escaped_strings();
  407. }
  408. void test_array_roundtrip()
  409. {
  410. pqxx::connection c;
  411. pqxx::work w{c};
  412. std::vector<int> const in{0, 1, 2, 3, 5};
  413. auto const r1{w.exec1("SELECT " + c.quote(in) + "::integer[]")};
  414. pqxx::array_parser parser{r1[0].view()};
  415. auto item{parser.get_next()};
  416. PQXX_CHECK_EQUAL(
  417. item.first, pqxx::array_parser::juncture::row_start,
  418. "Array did not start with row_start.");
  419. std::vector<int> out;
  420. for (item = parser.get_next();
  421. item.first == pqxx::array_parser::juncture::string_value;
  422. item = parser.get_next())
  423. {
  424. out.push_back(pqxx::from_string<int>(item.second));
  425. }
  426. PQXX_CHECK_EQUAL(
  427. item.first, pqxx::array_parser::juncture::row_end,
  428. "Array values did not end in row_end.");
  429. PQXX_CHECK_EQUAL(
  430. std::size(out), std::size(in), "Array came back with different length.");
  431. for (std::size_t i{0}; i < std::size(in); ++i)
  432. PQXX_CHECK_EQUAL(out[i], in[i], "Array element has changed.");
  433. item = parser.get_next();
  434. PQXX_CHECK_EQUAL(
  435. item.first, pqxx::array_parser::juncture::done,
  436. "Array did not end in done.");
  437. }
  438. PQXX_REGISTER_TEST(test_array_parse);
  439. PQXX_REGISTER_TEST(test_array_generate);
  440. PQXX_REGISTER_TEST(test_array_roundtrip);
  441. } // namespace