PageRenderTime 280ms CodeModel.GetById 68ms app.highlight 107ms RepoModel.GetById 56ms app.codeStats 1ms

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