PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/pq/tests/pq.cpp

http://github.com/mozy/mordor
C++ | 333 lines | 290 code | 42 blank | 1 comment | 7 complexity | 1e8a6e39700d6a41aa60823abbaf57ef MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "mordor/predef.h"
  3. #include <iostream>
  4. #include <boost/date_time/posix_time/posix_time_io.hpp>
  5. #include "mordor/config.h"
  6. #include "mordor/iomanager.h"
  7. #include "mordor/main.h"
  8. #include "mordor/pq/connection.h"
  9. #include "mordor/pq/exception.h"
  10. #include "mordor/pq/transaction.h"
  11. #include "mordor/version.h"
  12. #include "mordor/statistics.h"
  13. #include "mordor/streams/memory.h"
  14. #include "mordor/streams/transfer.h"
  15. #include "mordor/test/antxmllistener.h"
  16. #include "mordor/test/test.h"
  17. #include "mordor/test/stdoutlistener.h"
  18. using namespace Mordor;
  19. using namespace Mordor::PQ;
  20. using namespace Mordor::Test;
  21. static ConfigVar<std::string>::ptr g_xmlDirectory = Config::lookup<std::string>(
  22. "test.antxml.directory", std::string(), "Location to put XML files");
  23. std::string g_goodConnString;
  24. std::string g_badConnString;
  25. MORDOR_MAIN(int argc, char **argv)
  26. {
  27. if (argc < 2) {
  28. std::cerr << "Usage: " << argv[0]
  29. << " <connection string>"
  30. << std::endl;
  31. return 1;
  32. }
  33. g_goodConnString = argv[1];
  34. --argc;
  35. ++argv;
  36. Config::loadFromEnvironment();
  37. boost::shared_ptr<TestListener> listener;
  38. std::string xmlDirectory = g_xmlDirectory->val();
  39. if (!xmlDirectory.empty()) {
  40. if (xmlDirectory == ".")
  41. xmlDirectory.clear();
  42. listener.reset(new AntXMLListener(xmlDirectory));
  43. } else {
  44. listener.reset(new StdoutListener());
  45. }
  46. bool result;
  47. if (argc > 1) {
  48. result = runTests(testsForArguments(argc - 1, argv + 1), *listener);
  49. } else {
  50. result = runTests(*listener);
  51. }
  52. std::cout << Statistics::dump();
  53. return result ? 0 : 1;
  54. }
  55. #ifdef WINDOWS
  56. #define MORDOR_PQ_UNITTEST(TestName) \
  57. static void PQ_ ## TestName(IOManager *ioManager); \
  58. MORDOR_UNITTEST(PQ, TestName) \
  59. { \
  60. PQ_ ## TestName(NULL); \
  61. } \
  62. static void PQ_ ## TestName(IOManager *ioManager)
  63. #else
  64. #define MORDOR_PQ_UNITTEST(TestName) \
  65. static void PQ_ ## TestName(IOManager *ioManager); \
  66. MORDOR_UNITTEST(PQ, TestName ## Blocking) \
  67. { \
  68. PQ_ ## TestName(NULL); \
  69. } \
  70. MORDOR_UNITTEST(PQ, TestName ## Async) \
  71. { \
  72. \
  73. IOManager ioManager; \
  74. PQ_ ## TestName(&ioManager); \
  75. } \
  76. static void PQ_ ## TestName(IOManager *ioManager)
  77. #endif
  78. void constantQuery(const std::string &queryName = std::string(),
  79. IOManager *ioManager = NULL)
  80. {
  81. Connection conn(g_goodConnString, ioManager);
  82. PreparedStatement stmt = conn.prepare("SELECT 1, 'mordor'", queryName);
  83. Result result = stmt.execute();
  84. MORDOR_TEST_ASSERT_EQUAL(result.rows(), 1u);
  85. MORDOR_TEST_ASSERT_EQUAL(result.columns(), 2u);
  86. MORDOR_TEST_ASSERT_EQUAL(result.get<int>(0u, (size_t)0u), 1);
  87. MORDOR_TEST_ASSERT_EQUAL(result.get<long long>(0u, (size_t)0u), 1);
  88. MORDOR_TEST_ASSERT_EQUAL(result.get<const char *>(0u, 1u), "mordor");
  89. MORDOR_TEST_ASSERT_EQUAL(result.get<std::string>(0u, 1u), "mordor");
  90. }
  91. MORDOR_PQ_UNITTEST(constantQuery)
  92. { constantQuery(std::string(), ioManager); }
  93. MORDOR_PQ_UNITTEST(constantQueryPrepared)
  94. { constantQuery("constant", ioManager); }
  95. MORDOR_PQ_UNITTEST(invalidConnString)
  96. {
  97. MORDOR_TEST_ASSERT_EXCEPTION(Connection conn("garbage", ioManager),
  98. ConnectionException);
  99. }
  100. MORDOR_PQ_UNITTEST(invalidConnString2)
  101. {
  102. MORDOR_TEST_ASSERT_EXCEPTION(Connection conn("garbage=", ioManager), ConnectionException);
  103. }
  104. MORDOR_PQ_UNITTEST(invalidConnString3)
  105. {
  106. MORDOR_TEST_ASSERT_EXCEPTION(Connection conn("host=garbage", ioManager), ConnectionException);
  107. }
  108. MORDOR_PQ_UNITTEST(badConnString)
  109. {
  110. MORDOR_TEST_ASSERT_EXCEPTION(Connection conn(g_badConnString, ioManager), ConnectionException);
  111. }
  112. #ifndef WINDOWS
  113. #define closesocket close
  114. #endif
  115. void queryAfterDisconnect(IOManager *ioManager = NULL)
  116. {
  117. Connection conn(g_goodConnString, ioManager);
  118. closesocket(PQsocket(conn.conn()));
  119. MORDOR_TEST_ASSERT_EXCEPTION(conn.execute("SELECT 1"), ConnectionException);
  120. conn.reset();
  121. Result result = conn.execute("SELECT 1");
  122. MORDOR_TEST_ASSERT_EQUAL(result.rows(), 1u);
  123. MORDOR_TEST_ASSERT_EQUAL(result.columns(), 1u);
  124. MORDOR_TEST_ASSERT_EQUAL(result.get<int>(0u, (size_t)0u), 1);
  125. }
  126. MORDOR_PQ_UNITTEST(queryAfterDisconnect)
  127. { queryAfterDisconnect(ioManager); }
  128. void fillUsers(Connection &conn)
  129. {
  130. conn.execute("CREATE TEMPORARY TABLE users (id INTEGER, name TEXT, height SMALLINT, awesome BOOLEAN, company TEXT, gender CHAR, efficiency REAL, crazy DOUBLE PRECISION, sometime TIMESTAMP, version INTEGER[4])");
  131. conn.execute("INSERT INTO users VALUES (1, 'cody', 72, true, 'Mozy', 'M', .9, .75, '2009-05-19 15:53:45.123456', '{1,3,5,7}')");
  132. conn.execute("INSERT INTO users VALUES (2, 'brian', 70, false, NULL, 'M', .9, .25, NULL, '{}')");
  133. }
  134. template <class ParamType, class ExpectedType>
  135. void queryForParam(const std::string &query, ParamType param, size_t expectedCount,
  136. ExpectedType expected,
  137. const std::string &queryName = std::string(), IOManager *ioManager = NULL)
  138. {
  139. Connection conn(g_goodConnString, ioManager);
  140. fillUsers(conn);
  141. PreparedStatement stmt = conn.prepare(query, queryName);
  142. Result result = stmt.execute(param);
  143. MORDOR_TEST_ASSERT_EQUAL(result.rows(), expectedCount);
  144. MORDOR_TEST_ASSERT_EQUAL(result.columns(), 1u);
  145. MORDOR_TEST_ASSERT_EQUAL(result.get<ExpectedType>(0u, (size_t)0u), expected);
  146. }
  147. MORDOR_PQ_UNITTEST(queryForInt)
  148. { queryForParam("SELECT name FROM users WHERE id=$1", 2, 1u, "brian", std::string(), ioManager); }
  149. MORDOR_PQ_UNITTEST(queryForIntPrepared)
  150. { queryForParam("SELECT name FROM users WHERE id=$1::integer", 2, 1u, "brian", "constant", ioManager); }
  151. MORDOR_PQ_UNITTEST(queryForString)
  152. { queryForParam("SELECT id FROM users WHERE name=$1", "brian", 1u, 2, std::string(), ioManager); }
  153. MORDOR_PQ_UNITTEST(queryForStringPrepared)
  154. { queryForParam("SELECT id FROM users WHERE name=$1::text", "brian", 1u, 2, "constant", ioManager); }
  155. MORDOR_PQ_UNITTEST(queryForSmallInt)
  156. { queryForParam("SELECT id FROM users WHERE height=$1", (short)70, 1u, 2, std::string(), ioManager); }
  157. MORDOR_PQ_UNITTEST(queryForSmallIntPrepared)
  158. { queryForParam("SELECT id FROM users WHERE height=$1::smallint", (short)70, 1u, 2, "constant", ioManager); }
  159. MORDOR_PQ_UNITTEST(queryForBoolean)
  160. { queryForParam("SELECT id FROM users WHERE awesome=$1", false, 1u, 2, std::string(), ioManager); }
  161. MORDOR_PQ_UNITTEST(queryForBooleanPrepared)
  162. { queryForParam("SELECT id FROM users WHERE awesome=$1::boolean", false, 1u, 2, "constant", ioManager); }
  163. MORDOR_PQ_UNITTEST(queryForChar)
  164. { queryForParam("SELECT id FROM users WHERE gender=$1", 'M', 2u, 1, std::string(), ioManager); }
  165. MORDOR_PQ_UNITTEST(queryForCharPrepared)
  166. { queryForParam("SELECT id FROM users WHERE gender=$1::CHAR", 'M', 2u, 1, "constant", ioManager); }
  167. MORDOR_PQ_UNITTEST(queryForFloat)
  168. { queryForParam("SELECT efficiency FROM users WHERE efficiency=$1", .9f, 2u, .9f, std::string(), ioManager); }
  169. MORDOR_PQ_UNITTEST(queryForFloatPrepared)
  170. { queryForParam("SELECT efficiency FROM users WHERE efficiency=$1::REAL", .9f, 2u, .9f, "constant", ioManager); }
  171. MORDOR_PQ_UNITTEST(queryForDouble)
  172. { queryForParam("SELECT crazy FROM users WHERE crazy=$1", .75, 1u, .75, std::string(), ioManager); }
  173. MORDOR_PQ_UNITTEST(queryForDoublePrepared)
  174. { queryForParam("SELECT crazy FROM users WHERE crazy=$1::DOUBLE PRECISION", .75, 1u, .75, "constant", ioManager); }
  175. MORDOR_PQ_UNITTEST(queryForIntArray)
  176. {
  177. std::vector<int> expected(4);
  178. expected[0] = 1; expected[1] = 3; expected[2] = 5; expected[3] = 7;
  179. queryForParam("SELECT version FROM users WHERE id=$1", 1, 1u, expected, std::string(), ioManager);
  180. }
  181. MORDOR_PQ_UNITTEST(queryForIntArrayPrepared)
  182. {
  183. std::vector<int> expected(4);
  184. expected[0] = 1; expected[1] = 3; expected[2] = 5; expected[3] = 7;
  185. queryForParam("SELECT version FROM users WHERE id=$1::INTEGER", 1, 1u, expected, "constant", ioManager);
  186. }
  187. MORDOR_PQ_UNITTEST(queryForEmptyIntArray)
  188. { queryForParam("SELECT version FROM users WHERE id=$1", 2, 1u, std::vector<int>(), std::string(), ioManager); }
  189. MORDOR_PQ_UNITTEST(queryForEmptyIntArrayPrepared)
  190. { queryForParam("SELECT version FROM users WHERE id=$1::INTEGER", 2, 1u, std::vector<int>(), "constant", ioManager); }
  191. static const boost::posix_time::ptime thetime(
  192. boost::gregorian::date(2009, 05, 19),
  193. boost::posix_time::hours(15) + boost::posix_time::minutes(53) +
  194. boost::posix_time::seconds(45) + boost::posix_time::microseconds(123456));
  195. static const boost::posix_time::ptime nulltime;
  196. MORDOR_PQ_UNITTEST(queryForTimestamp)
  197. { queryForParam("SELECT sometime FROM users WHERE sometime=$1", thetime, 1u, thetime, std::string(), ioManager); }
  198. MORDOR_PQ_UNITTEST(queryForTimestampPrepared)
  199. { queryForParam("SELECT sometime FROM users WHERE sometime=$1::TIMESTAMP", thetime, 1u, thetime, "constant", ioManager); }
  200. MORDOR_PQ_UNITTEST(queryForNullTimestamp)
  201. { queryForParam("SELECT sometime FROM users WHERE sometime IS NULL OR sometime=$1", nulltime, 1u, nulltime, std::string(), ioManager); }
  202. MORDOR_PQ_UNITTEST(queryForNullTimestampPrepared)
  203. { queryForParam("SELECT sometime FROM users WHERE sometime IS NULL OR sometime=$1", nulltime, 1u, nulltime, "constant", ioManager); }
  204. MORDOR_UNITTEST(PQ, transactionCommits)
  205. {
  206. Connection conn(g_goodConnString);
  207. fillUsers(conn);
  208. Transaction t(conn);
  209. conn.execute("UPDATE users SET name='tom' WHERE id=1");
  210. t.commit();
  211. Result result = conn.execute("SELECT name FROM users WHERE id=1");
  212. MORDOR_TEST_ASSERT_EQUAL(result.rows(), 1u);
  213. MORDOR_TEST_ASSERT_EQUAL(result.columns(), 1u);
  214. MORDOR_TEST_ASSERT_EQUAL(result.get<const char *>(0u, (size_t)0u), "tom");
  215. }
  216. MORDOR_UNITTEST(PQ, transactionRollsback)
  217. {
  218. Connection conn(g_goodConnString);
  219. fillUsers(conn);
  220. Transaction t(conn);
  221. conn.execute("UPDATE users SET name='tom' WHERE id=1");
  222. t.rollback();
  223. Result result = conn.execute("SELECT name FROM users WHERE id=1");
  224. MORDOR_TEST_ASSERT_EQUAL(result.rows(), 1u);
  225. MORDOR_TEST_ASSERT_EQUAL(result.columns(), 1u);
  226. MORDOR_TEST_ASSERT_EQUAL(result.get<const char *>(0u, (size_t)0u), "cody");
  227. }
  228. MORDOR_UNITTEST(PQ, transactionRollsbackAutomatically)
  229. {
  230. Connection conn(g_goodConnString);
  231. fillUsers(conn);
  232. {
  233. Transaction t(conn);
  234. conn.execute("UPDATE users SET name='tom' WHERE id=1");
  235. }
  236. Result result = conn.execute("SELECT name FROM users WHERE id=1");
  237. MORDOR_TEST_ASSERT_EQUAL(result.rows(), 1u);
  238. MORDOR_TEST_ASSERT_EQUAL(result.columns(), 1u);
  239. MORDOR_TEST_ASSERT_EQUAL(result.get<const char *>(0u, (size_t)0u), "cody");
  240. }
  241. static void copyIn(IOManager *ioManager = NULL)
  242. {
  243. Connection conn(g_goodConnString, ioManager);
  244. conn.execute("CREATE TEMP TABLE stuff (id INTEGER, name TEXT)");
  245. Stream::ptr stream = conn.copyIn("stuff").csv()();
  246. stream->write("1,cody\n");
  247. stream->write("2,tom\n");
  248. stream->write("3,brian\n");
  249. stream->write("4,jeremy\n");
  250. stream->write("5,zach\n");
  251. stream->write("6,paul\n");
  252. stream->write("7,alen\n");
  253. stream->write("8,jt\n");
  254. stream->write("9,jon\n");
  255. stream->write("10,jacob\n");
  256. stream->close();
  257. Result result = conn.execute("SELECT COUNT(*) FROM stuff");
  258. MORDOR_TEST_ASSERT_EQUAL(result.rows(), 1u);
  259. MORDOR_TEST_ASSERT_EQUAL(result.columns(), 1u);
  260. MORDOR_TEST_ASSERT_EQUAL(result.get<long long>(0u, (size_t)0u), 10);
  261. result = conn.execute("SELECT SUM(id) FROM stuff");
  262. MORDOR_TEST_ASSERT_EQUAL(result.rows(), 1u);
  263. MORDOR_TEST_ASSERT_EQUAL(result.columns(), 1u);
  264. MORDOR_TEST_ASSERT_EQUAL(result.get<long long>(0u, (size_t)0u), 55);
  265. }
  266. MORDOR_PQ_UNITTEST(copyIn)
  267. { copyIn(ioManager); }
  268. static void copyOut(IOManager *ioManager = NULL)
  269. {
  270. Connection conn(g_goodConnString, ioManager);
  271. conn.execute("CREATE TEMP TABLE country (code TEXT, name TEXT)");
  272. PreparedStatement stmt = conn.prepare("INSERT INTO country VALUES($1, $2)",
  273. "insertcountry");
  274. Transaction transaction(conn);
  275. stmt.execute("AF", "AFGHANISTAN");
  276. stmt.execute("AL", "ALBANIA");
  277. stmt.execute("DZ", "ALGERIA");
  278. stmt.execute("ZM", "ZAMBIA");
  279. stmt.execute("ZW", "ZIMBABWE");
  280. Stream::ptr stream = conn.copyOut("country").csv().delimiter('|')();
  281. MemoryStream output;
  282. transferStream(stream, output);
  283. MORDOR_TEST_ASSERT(output.buffer() ==
  284. "AF|AFGHANISTAN\n"
  285. "AL|ALBANIA\n"
  286. "DZ|ALGERIA\n"
  287. "ZM|ZAMBIA\n"
  288. "ZW|ZIMBABWE\n");
  289. }
  290. MORDOR_PQ_UNITTEST(copyOut)
  291. { copyOut(ioManager); }