pgsql_exchange_unittest.cc 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. // Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <dhcpsrv/pgsql_connection.h>
  8. #include <dhcpsrv/pgsql_exchange.h>
  9. #include <boost/lexical_cast.hpp>
  10. #include <gtest/gtest.h>
  11. #include <sstream>
  12. #include <vector>
  13. using namespace isc;
  14. using namespace isc::dhcp;
  15. namespace {
  16. /// @brief Verifies the ability to add various data types to
  17. /// the bind array.
  18. TEST(PsqlBindArray, addDataTest) {
  19. PsqlBindArray b;
  20. // Declare a vector to add. Vectors are not currently duplicated
  21. // So they will go out of scope, unless caller ensures it.
  22. std::vector<uint8_t> bytes;
  23. for (int i = 0; i < 10; i++) {
  24. bytes.push_back(i+1);
  25. }
  26. // Declare a string
  27. std::string not_temp_str("just a string");
  28. // Now add all the items within a different scope. Everything should
  29. // still be valid once we exit this scope.
  30. {
  31. // Add a const char*
  32. b.add("booya!");
  33. // Add the non temporary string
  34. b.add(not_temp_str);
  35. // Add a temporary string
  36. b.addTempString("walah walah washington");
  37. // Add a one byte int
  38. uint8_t small_int = 25;
  39. b.add(small_int);
  40. // Add a four byte int
  41. int reg_int = 376;
  42. b.add(reg_int);
  43. // Add a eight byte unsigned int
  44. uint64_t big_int = 48786749032;
  45. b.add(big_int);
  46. // Add boolean true and false
  47. b.add((bool)(1));
  48. b.add((bool)(0));
  49. // Add IP addresses
  50. b.add(isc::asiolink::IOAddress("192.2.15.34"));
  51. b.add(isc::asiolink::IOAddress("3001::1"));
  52. // Add the vector
  53. b.add(bytes);
  54. }
  55. // We've left bind scope, everything should be intact.
  56. std::string expected =
  57. "0 : \"booya!\"\n"
  58. "1 : \"just a string\"\n"
  59. "2 : \"walah walah washington\"\n"
  60. "3 : \"25\"\n"
  61. "4 : \"376\"\n"
  62. "5 : \"48786749032\"\n"
  63. "6 : \"TRUE\"\n"
  64. "7 : \"FALSE\"\n"
  65. "8 : \"3221360418\"\n"
  66. "9 : \"3001::1\"\n"
  67. "10 : 0x0102030405060708090a\n";
  68. EXPECT_EQ(expected, b.toText());
  69. }
  70. /// @brief Defines a pointer to a PgSqlConnection
  71. typedef boost::shared_ptr<PgSqlConnection> PgSqlConnectionPtr;
  72. /// @brief Defines a pointer to a PgSqlResult
  73. typedef boost::shared_ptr<PgSqlResult> PgSqlResultPtr;
  74. /// @brief Fixture for exercising basic PostgreSQL operations and data types
  75. ///
  76. /// This class is intended to be used to verify basic operations and to
  77. /// verify that each PostgreSQL data type currently used by Kea, can be
  78. /// correctly written to and read from PostgreSQL. Rather than use tables
  79. /// that belong to Kea the schema proper, it creates its own. Currently it
  80. /// consists of a single table, called "basics" which contains one column for
  81. /// each of the supported data types.
  82. ///
  83. /// It creates the schema during construction, deletes it upon destruction, and
  84. /// provides functions for executing SQL statements, executing prepared
  85. /// statements, fetching all rows in the table, and deleting all the rows in
  86. /// the table.
  87. class PgSqlBasicsTest : public ::testing::Test {
  88. public:
  89. /// @brief Column index for each column
  90. enum BasicColIndex {
  91. ID_COL,
  92. BOOL_COL,
  93. BYTEA_COL,
  94. BIGINT_COL,
  95. SMALLINT_COL,
  96. INT_COL,
  97. TEXT_COL,
  98. TIMESTAMP_COL,
  99. VARCHAR_COL,
  100. NUM_BASIC_COLS
  101. };
  102. /// @brief Constructor
  103. ///
  104. /// Creates the database connection, opens the database, and destroys
  105. /// the table (if present) and then recreates it.
  106. PgSqlBasicsTest() : expectedColNames_(NUM_BASIC_COLS) {
  107. // Create database connection parameter list
  108. PgSqlConnection::ParameterMap params;
  109. params["name"] = "keatest";
  110. params["user"] = "keatest";
  111. params["password"] = "keatest";
  112. // Create and open the database connection
  113. conn_.reset(new PgSqlConnection(params));
  114. conn_->openDatabase();
  115. // Create the list of expected column names
  116. expectedColNames_[ID_COL] = "id";
  117. expectedColNames_[BOOL_COL] = "bool_col";
  118. expectedColNames_[BYTEA_COL] = "bytea_col";
  119. expectedColNames_[BIGINT_COL] = "bigint_col";
  120. expectedColNames_[SMALLINT_COL] = "smallint_col";
  121. expectedColNames_[INT_COL] = "int_col";
  122. expectedColNames_[TEXT_COL] = "text_col";
  123. expectedColNames_[TIMESTAMP_COL] = "timestamp_col";
  124. expectedColNames_[VARCHAR_COL] = "varchar_col";
  125. destroySchema();
  126. createSchema();
  127. }
  128. /// @brief Destructor
  129. ///
  130. /// Destroys the table. The database resources are freed and the connection
  131. /// closed by the destruction of conn_.
  132. virtual ~PgSqlBasicsTest () {
  133. destroySchema();
  134. }
  135. /// @brief Gets the expected name of the column for a given column index
  136. ///
  137. /// Returns the name of column as we expect it to be when the column is
  138. /// fetched from the database.
  139. ///
  140. /// @param col index of the desired column
  141. ///
  142. /// @return string containing the column name
  143. ///
  144. /// @throw BadValue if the index is out of range
  145. const std::string& expectedColumnName(int col) {
  146. if (col < 0 || col >= NUM_BASIC_COLS) {
  147. isc_throw(BadValue,
  148. "definedColunName: invalid column value" << col);
  149. }
  150. return (expectedColNames_[col]);
  151. }
  152. /// @brief Creates the basics table
  153. /// Asserts if the creation step fails
  154. void createSchema() {
  155. // One column for OID type, plus an auto-increment
  156. const char* sql =
  157. "CREATE TABLE basics ( "
  158. " id SERIAL PRIMARY KEY NOT NULL, "
  159. " bool_col BOOLEAN, "
  160. " bytea_col BYTEA, "
  161. " bigint_col BIGINT, "
  162. " smallint_col SMALLINT, "
  163. " int_col INT, "
  164. " text_col TEXT, "
  165. " timestamp_col TIMESTAMP WITH TIME ZONE, "
  166. " varchar_col VARCHAR(255) "
  167. "); ";
  168. PgSqlResult r(PQexec(*conn_, sql));
  169. ASSERT_EQ(PQresultStatus(r), PGRES_COMMAND_OK)
  170. << " create basics table failed: " << PQerrorMessage(*conn_);
  171. }
  172. /// @brief Destroys the basics table
  173. /// Asserts if the destruction fails
  174. void destroySchema() {
  175. if (conn_) {
  176. PgSqlResult r(PQexec(*conn_, "DROP TABLE IF EXISTS basics;"));
  177. ASSERT_EQ(PQresultStatus(r), PGRES_COMMAND_OK)
  178. << " drop basics table failed: " << PQerrorMessage(*conn_);
  179. }
  180. }
  181. /// @brief Executes a SQL statement and tests for an expected outcome
  182. ///
  183. /// @param r pointer which will contain the result set returned by the
  184. /// statement's execution.
  185. /// @param sql string containing the SQL statement text. Note that
  186. /// PostgreSQL supports executing text which contains more than one SQL
  187. /// statement separated by semicolons.
  188. /// @param exp_outcome expected status value returned with within the
  189. /// result set such as PGRES_COMMAND_OK, PGRES_TUPLES_OK.
  190. /// @lineno line number from where the call was invoked
  191. ///
  192. /// Asserts if the result set status does not equal the expected outcome.
  193. void runSql(PgSqlResultPtr& r, const std::string& sql, int exp_outcome,
  194. int lineno) {
  195. r.reset(new PgSqlResult(PQexec(*conn_, sql.c_str())));
  196. ASSERT_EQ(PQresultStatus(*r), exp_outcome)
  197. << " runSql at line: " << lineno << " failed, sql:[" << sql
  198. << "]\n reason: " << PQerrorMessage(*conn_);
  199. }
  200. /// @brief Executes a SQL statement and tests for an expected outcome
  201. ///
  202. /// @param r pointer which will contain the result set returned by the
  203. /// statement's execution.
  204. /// @param statement statement descriptor of the prepared statement
  205. /// to execute.
  206. /// @param bind_array bind array containing the input values to submit
  207. /// along with the statement
  208. /// @param exp_outcome expected status value returned with within the
  209. /// result set such as PGRES_COMMAND_OK, PGRES_TUPLES_OK.
  210. /// @lineno line number from where the call was invoked
  211. ///
  212. /// Asserts if the result set status does not equal the expected outcome.
  213. void runPreparedStatement(PgSqlResultPtr& r,
  214. PgSqlTaggedStatement& statement,
  215. PsqlBindArrayPtr bind_array, int exp_outcome,
  216. int lineno) {
  217. r.reset(new PgSqlResult(PQexecPrepared(*conn_, statement.name,
  218. statement.nbparams,
  219. &bind_array->values_[0],
  220. &bind_array->lengths_[0],
  221. &bind_array->formats_[0], 0)));
  222. ASSERT_EQ(PQresultStatus(*r), exp_outcome)
  223. << " runPreparedStatement at line: " << lineno
  224. << " statement name:[" << statement.name
  225. << "]\n reason: " << PQerrorMessage(*conn_);
  226. }
  227. /// @brief Fetches all of the rows currently in the table
  228. ///
  229. /// Executes a select statement which returns all of the rows in the
  230. /// basics table, in their order of insertion. Each row contains all
  231. /// of the defined columns, in the order they are defined.
  232. ///
  233. /// @param r pointer which will contain the result set returned by the
  234. /// statement's execution.
  235. /// @param exp_rows expected number of rows fetched. (This can be 0).
  236. /// @lineno line number from where the call was invoked
  237. ///
  238. /// Asserts if the result set status does not equal the expected outcome.
  239. void fetchRows(PgSqlResultPtr& r, int exp_rows, int line) {
  240. std::string sql =
  241. "SELECT"
  242. " id, bool_col, bytea_col, bigint_col, smallint_col, "
  243. " int_col, text_col,"
  244. " extract(epoch from timestamp_col)::bigint as timestamp_col,"
  245. " varchar_col FROM basics";
  246. runSql(r, sql, PGRES_TUPLES_OK, line);
  247. ASSERT_EQ(r->getRows(), exp_rows) << "fetch at line: " << line
  248. << " wrong row count, expected: " << exp_rows
  249. << " , have: " << r->getRows();
  250. }
  251. /// @brief Database connection
  252. PgSqlConnectionPtr conn_;
  253. /// @brief List of column names as we expect them to be in fetched rows
  254. std::vector<std::string> expectedColNames_;
  255. };
  256. // Macros defined to ease passing invocation line number for output tracing
  257. // (Yes I could have used scoped tracing but that's so ugly in code...)
  258. #define RUN_SQL(a,b,c) (runSql(a,b,c, __LINE__))
  259. #define RUN_PREP(a,b,c,d) (runPreparedStatement(a,b,c,d, __LINE__))
  260. #define FETCH_ROWS(a,b) (fetchRows(a,b,__LINE__))
  261. #define WIPE_ROWS(a) (RUN_SQL(a, "DELETE FROM BASICS", PGRES_COMMAND_OK))
  262. /// @brief Verifies that PgResultSet row and colum meta-data is correct
  263. TEST_F(PgSqlBasicsTest, rowColumnBasics) {
  264. // We fetch the table contents, which at this point should be no rows.
  265. PgSqlResultPtr r;
  266. FETCH_ROWS(r, 0);
  267. // Column meta-data is determined by the select statement and is
  268. // present whether or not any rows were returned.
  269. EXPECT_EQ(r->getCols(), NUM_BASIC_COLS);
  270. // Negative indexes should be out of range. We test negative values
  271. // as PostgreSQL functions accept column values as type int.
  272. EXPECT_THROW(r->colCheck(-1), DbOperationError);
  273. // Iterate over the column indexes verifying:
  274. // 1. the column is valid
  275. // 2. the result set column name matches the expected column name
  276. for (int i = 0; i < NUM_BASIC_COLS; i++) {
  277. EXPECT_NO_THROW(r->colCheck(i));
  278. EXPECT_EQ(r->getColumnLabel(i), expectedColumnName(i));
  279. }
  280. // Verify above range column value is detected.
  281. EXPECT_THROW(r->colCheck(NUM_BASIC_COLS), DbOperationError);
  282. // Verify the fetching a column label for out of range columns
  283. // do NOT throw.
  284. std::string label;
  285. ASSERT_NO_THROW(label = r->getColumnLabel(-1));
  286. EXPECT_EQ(label, "Unknown column:-1");
  287. ASSERT_NO_THROW(label = r->getColumnLabel(NUM_BASIC_COLS));
  288. std::ostringstream os;
  289. os << "Unknown column:" << NUM_BASIC_COLS;
  290. EXPECT_EQ(label, os.str());
  291. // Verify row count and checking. With an empty result set all values of
  292. // row are invalid.
  293. EXPECT_EQ(r->getRows(), 0);
  294. EXPECT_THROW(r->rowCheck(-1), DbOperationError);
  295. EXPECT_THROW(r->rowCheck(0), DbOperationError);
  296. EXPECT_THROW(r->rowCheck(1), DbOperationError);
  297. // Verify Row-column check will always fail with an empty result set.
  298. EXPECT_THROW(r->rowColCheck(-1, 1), DbOperationError);
  299. EXPECT_THROW(r->rowColCheck(0, 1), DbOperationError);
  300. EXPECT_THROW(r->rowColCheck(1, 1), DbOperationError);
  301. // Insert three minimal rows. We don't really care about column content
  302. // for this test.
  303. int num_rows = 3;
  304. for (int i = 0; i < num_rows; i++) {
  305. RUN_SQL(r, "INSERT INTO basics (bool_col) VALUES ('t')",
  306. PGRES_COMMAND_OK);
  307. }
  308. // Fetch the newly created rows.
  309. FETCH_ROWS(r, num_rows);
  310. // Verify we row count and checking
  311. EXPECT_EQ(r->getRows(), num_rows);
  312. EXPECT_THROW(r->rowCheck(-1), DbOperationError);
  313. // Iterate over the row count, verifying that expected rows are valid
  314. for (int i = 0; i < num_rows; i++) {
  315. EXPECT_NO_THROW(r->rowCheck(i));
  316. EXPECT_NO_THROW(r->rowColCheck(i, 0));
  317. }
  318. // Verify an above range row is detected.
  319. EXPECT_THROW(r->rowCheck(num_rows), DbOperationError);
  320. }
  321. /// @brief Verify that we can read and write BOOL columns
  322. TEST_F(PgSqlBasicsTest, boolTest) {
  323. // Create a prepared statement for inserting bool_col
  324. const char* st_name = "bool_insert";
  325. PgSqlTaggedStatement statement[] = {
  326. {1, { OID_BOOL }, st_name,
  327. "INSERT INTO BASICS (bool_col) values ($1)" }
  328. };
  329. ASSERT_NO_THROW(conn_->prepareStatement(statement[0]));
  330. bool bools[] = { true, false };
  331. PsqlBindArrayPtr bind_array(new PsqlBindArray());
  332. PgSqlResultPtr r;
  333. // Insert bool rows
  334. for (int i = 0; i < 2; ++i) {
  335. bind_array.reset(new PsqlBindArray());
  336. bind_array->add(bools[i]);
  337. RUN_PREP(r,statement[0], bind_array, PGRES_COMMAND_OK);
  338. }
  339. // Fetch the newly inserted rows.
  340. FETCH_ROWS(r, 2);
  341. // Verify the fetched bool values are what we expect.
  342. bool fetched_bool;
  343. int row = 0;
  344. for ( ; row < 2; ++row ) {
  345. // Verify the column is not null.
  346. ASSERT_FALSE(PgSqlExchange::isColumnNull(*r, row, BOOL_COL));
  347. // Fetch and verify the column value
  348. fetched_bool = !bools[row];
  349. ASSERT_NO_THROW(PgSqlExchange::getColumnValue(*r, row, BOOL_COL,
  350. fetched_bool));
  351. EXPECT_EQ(fetched_bool, bools[row]);
  352. }
  353. // While we here, verify that bad row throws
  354. ASSERT_THROW(PgSqlExchange::getColumnValue(*r, row, 1, fetched_bool),
  355. DbOperationError);
  356. // Clean out the table
  357. WIPE_ROWS(r);
  358. // Verify we can insert a NULL boolean
  359. bind_array.reset(new PsqlBindArray());
  360. bind_array->addNull();
  361. // Run the insert with the bind array.
  362. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  363. // Fetch the newly inserted row.
  364. FETCH_ROWS(r, 1);
  365. // Verify the column is null.
  366. ASSERT_TRUE(PgSqlExchange::isColumnNull(*r, 0, 1));
  367. }
  368. /// @brief Verify that we can read and write BYTEA columns
  369. TEST_F(PgSqlBasicsTest, byteaTest) {
  370. const char* st_name = "bytea_insert";
  371. PgSqlTaggedStatement statement[] = {
  372. {1, { OID_BYTEA }, st_name,
  373. "INSERT INTO BASICS (bytea_col) values ($1)" }
  374. };
  375. ASSERT_NO_THROW(conn_->prepareStatement(statement[0]));
  376. const uint8_t bytes[] = {
  377. 0x01, 0x02, 0x03, 0x04
  378. };
  379. std::vector<uint8_t> vbytes(bytes, bytes + sizeof(bytes));
  380. // Verify we can insert bytea from a vector
  381. PsqlBindArrayPtr bind_array(new PsqlBindArray());
  382. PgSqlResultPtr r;
  383. bind_array->add(vbytes);
  384. RUN_PREP(r,statement[0], bind_array, PGRES_COMMAND_OK);
  385. // Verify we can insert bytea from a buffer.
  386. bind_array.reset(new PsqlBindArray());
  387. bind_array->add(bytes, sizeof(bytes));
  388. RUN_PREP(r,statement[0], bind_array, PGRES_COMMAND_OK);
  389. // Fetch the newly inserted rows.
  390. int num_rows = 2;
  391. FETCH_ROWS(r, num_rows);
  392. uint8_t fetched_bytes[sizeof(bytes)];
  393. size_t byte_count;
  394. int row = 0;
  395. for ( ; row < num_rows; ++row) {
  396. // Verify the column is not null.
  397. ASSERT_FALSE(PgSqlExchange::isColumnNull(*r, row, BYTEA_COL));
  398. // Extract the data into a correctly sized buffer
  399. memset(fetched_bytes, 0, sizeof(fetched_bytes));
  400. ASSERT_NO_THROW(PgSqlExchange::convertFromBytea(*r, row, BYTEA_COL,
  401. fetched_bytes,
  402. sizeof(fetched_bytes),
  403. byte_count));
  404. // Verify the data is correct
  405. ASSERT_EQ(byte_count, sizeof(bytes));
  406. for (int i = 0; i < sizeof(bytes); i++) {
  407. ASSERT_EQ(bytes[i], fetched_bytes[i]);
  408. }
  409. }
  410. // While we here, verify that bad row throws
  411. ASSERT_THROW(PgSqlExchange::convertFromBytea(*r, row, BYTEA_COL,
  412. fetched_bytes,
  413. sizeof(fetched_bytes),
  414. byte_count), DbOperationError);
  415. // Verify that too small of a buffer throws
  416. ASSERT_THROW(PgSqlExchange::convertFromBytea(*r, 0, BYTEA_COL,
  417. fetched_bytes,
  418. sizeof(fetched_bytes) - 1,
  419. byte_count), DbOperationError);
  420. // Clean out the table
  421. WIPE_ROWS(r);
  422. // Verify we can insert a NULL for a bytea column
  423. bind_array.reset(new PsqlBindArray());
  424. bind_array->addNull(PsqlBindArray::BINARY_FMT);
  425. RUN_PREP(r,statement[0], bind_array, PGRES_COMMAND_OK);
  426. // Fetch the newly inserted row.
  427. FETCH_ROWS(r, 1);
  428. // Verify the column is null.
  429. ASSERT_TRUE(PgSqlExchange::isColumnNull(*r, 0, BYTEA_COL));
  430. // Verify that fetching a NULL bytea, returns 0 byte count
  431. ASSERT_NO_THROW(PgSqlExchange::convertFromBytea(*r, 0, BYTEA_COL,
  432. fetched_bytes,
  433. sizeof(fetched_bytes),
  434. byte_count));
  435. EXPECT_EQ(byte_count, 0);
  436. }
  437. /// @brief Verify that we can read and write BIGINT columns
  438. TEST_F(PgSqlBasicsTest, bigIntTest) {
  439. // Create a prepared statement for inserting BIGINT
  440. const char* st_name = "bigint_insert";
  441. PgSqlTaggedStatement statement[] = {
  442. { 1, { OID_INT8 }, st_name,
  443. "INSERT INTO BASICS (bigint_col) values ($1)" }
  444. };
  445. ASSERT_NO_THROW(conn_->prepareStatement(statement[0]));
  446. // Build our reference list of reference values
  447. std::vector<int64_t> ints;
  448. ints.push_back(-1);
  449. ints.push_back(0);
  450. ints.push_back(0x7fffffffffffffff);
  451. ints.push_back(0xffffffffffffffff);
  452. // Insert a row for each reference value
  453. PsqlBindArrayPtr bind_array;
  454. PgSqlResultPtr r;
  455. for (int i = 0; i < ints.size(); ++i) {
  456. bind_array.reset(new PsqlBindArray());
  457. bind_array->add(ints[i]);
  458. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  459. }
  460. // Fetch the newly inserted rows.
  461. FETCH_ROWS(r, ints.size());
  462. // Iterate over the rows, verifying each value against its reference
  463. int64_t fetched_int;
  464. int row = 0;
  465. for ( ; row < ints.size(); ++row ) {
  466. // Verify the column is not null.
  467. ASSERT_FALSE(PgSqlExchange::isColumnNull(*r, row, BIGINT_COL));
  468. // Fetch and verify the column value
  469. fetched_int = 777;
  470. ASSERT_NO_THROW(PgSqlExchange::getColumnValue(*r, row, BIGINT_COL,
  471. fetched_int));
  472. EXPECT_EQ(fetched_int, ints[row]);
  473. }
  474. // While we here, verify that bad row throws
  475. ASSERT_THROW(PgSqlExchange::getColumnValue(*r, row, BIGINT_COL,
  476. fetched_int), DbOperationError);
  477. // Clean out the table
  478. WIPE_ROWS(r);
  479. // Verify we can insert a NULL value.
  480. bind_array.reset(new PsqlBindArray());
  481. bind_array->addNull();
  482. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  483. // Fetch the newly inserted row.
  484. FETCH_ROWS(r, 1);
  485. // Verify the column is null.
  486. ASSERT_TRUE(PgSqlExchange::isColumnNull(*r, 0, BIGINT_COL));
  487. }
  488. /// @brief Verify that we can read and write SMALLINT columns
  489. TEST_F(PgSqlBasicsTest, smallIntTest) {
  490. // Create a prepared statement for inserting a SMALLINT
  491. const char* st_name = "smallint_insert";
  492. PgSqlTaggedStatement statement[] = {
  493. { 1, { OID_INT2 }, st_name,
  494. "INSERT INTO BASICS (smallint_col) values ($1)" }
  495. };
  496. ASSERT_NO_THROW(conn_->prepareStatement(statement[0]));
  497. // Build our reference list of reference values
  498. std::vector<int16_t>ints;
  499. ints.push_back(-1);
  500. ints.push_back(0);
  501. ints.push_back(0x7fff);
  502. ints.push_back(0xffff);
  503. // Insert a row for each reference value
  504. PsqlBindArrayPtr bind_array;
  505. PgSqlResultPtr r;
  506. for (int i = 0; i < ints.size(); ++i) {
  507. bind_array.reset(new PsqlBindArray());
  508. bind_array->add(ints[i]);
  509. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  510. }
  511. // Fetch the newly inserted rows.
  512. FETCH_ROWS(r, ints.size());
  513. // Iterate over the rows, verifying each value against its reference
  514. int16_t fetched_int;
  515. int row = 0;
  516. for ( ; row < ints.size(); ++row ) {
  517. // Verify the column is not null.
  518. ASSERT_FALSE(PgSqlExchange::isColumnNull(*r, row, SMALLINT_COL));
  519. // Fetch and verify the column value
  520. fetched_int = 777;
  521. ASSERT_NO_THROW(PgSqlExchange::getColumnValue(*r, row, SMALLINT_COL,
  522. fetched_int));
  523. EXPECT_EQ(fetched_int, ints[row]);
  524. }
  525. // While we here, verify that bad row throws
  526. ASSERT_THROW(PgSqlExchange::getColumnValue(*r, row, SMALLINT_COL,
  527. fetched_int),
  528. DbOperationError);
  529. // Clean out the table
  530. WIPE_ROWS(r);
  531. // Verify we can insert a NULL value.
  532. bind_array.reset(new PsqlBindArray());
  533. bind_array->addNull();
  534. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  535. // Fetch the newly inserted row.
  536. FETCH_ROWS(r, 1);
  537. // Verify the column is null.
  538. ASSERT_TRUE(PgSqlExchange::isColumnNull(*r, 0, SMALLINT_COL));
  539. }
  540. /// @brief Verify that we can read and write INT columns
  541. TEST_F(PgSqlBasicsTest, intTest) {
  542. // Create a prepared statement for inserting an INT
  543. const char* st_name = "int_insert";
  544. PgSqlTaggedStatement statement[] = {
  545. { 1, { OID_INT4 }, st_name,
  546. "INSERT INTO BASICS (int_col) values ($1)" }
  547. };
  548. ASSERT_NO_THROW(conn_->prepareStatement(statement[0]));
  549. // Build our reference list of reference values
  550. std::vector<int32_t> ints;
  551. ints.push_back(-1);
  552. ints.push_back(0);
  553. ints.push_back(0x7fffffff);
  554. ints.push_back(0xffffffff);
  555. // Insert a row for each reference value
  556. PsqlBindArrayPtr bind_array;
  557. PgSqlResultPtr r;
  558. for (int i = 0; i < ints.size(); ++i) {
  559. bind_array.reset(new PsqlBindArray());
  560. bind_array->add(ints[i]);
  561. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  562. }
  563. // Fetch the newly inserted rows.
  564. FETCH_ROWS(r, ints.size());
  565. // Iterate over the rows, verifying each value against its reference
  566. int32_t fetched_int;
  567. int row = 0;
  568. for ( ; row < ints.size(); ++row ) {
  569. // Verify the column is not null.
  570. ASSERT_FALSE(PgSqlExchange::isColumnNull(*r, row, INT_COL));
  571. // Fetch and verify the column value
  572. fetched_int = 777;
  573. ASSERT_NO_THROW(PgSqlExchange::getColumnValue(*r, row, INT_COL,
  574. fetched_int));
  575. EXPECT_EQ(fetched_int, ints[row]);
  576. }
  577. // While we here, verify that bad row throws
  578. ASSERT_THROW(PgSqlExchange::getColumnValue(*r, row, INT_COL, fetched_int),
  579. DbOperationError);
  580. // Clean out the table
  581. WIPE_ROWS(r);
  582. // Verify we can insert a NULL value.
  583. bind_array.reset(new PsqlBindArray());
  584. bind_array->addNull();
  585. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  586. // Fetch the newly inserted rows
  587. FETCH_ROWS(r, 1);
  588. // Verify the column is null.
  589. ASSERT_TRUE(PgSqlExchange::isColumnNull(*r, 0, INT_COL));
  590. }
  591. /// @brief Verify that we can read and write TEXT columns
  592. TEST_F(PgSqlBasicsTest, textTest) {
  593. // Create a prepared statement for inserting TEXT
  594. PgSqlTaggedStatement statement[] = {
  595. { 1, { OID_TEXT }, "text_insert",
  596. "INSERT INTO BASICS (text_col) values ($1)" }
  597. };
  598. ASSERT_NO_THROW(conn_->prepareStatement(statement[0]));
  599. // Our reference string.
  600. std::string ref_string = "This is a text string";
  601. // Insert the reference from std::string
  602. PsqlBindArrayPtr bind_array;
  603. PgSqlResultPtr r;
  604. bind_array.reset(new PsqlBindArray());
  605. bind_array->add(ref_string);
  606. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  607. // Insert the reference from a buffer
  608. bind_array.reset(new PsqlBindArray());
  609. bind_array->add(ref_string.c_str());
  610. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  611. // Fetch the newly inserted rows.
  612. FETCH_ROWS(r, 2);
  613. // Iterate over the rows, verifying the value against the reference
  614. std::string fetched_str;
  615. int row = 0;
  616. for ( ; row < 2; ++row ) {
  617. // Verify the column is not null.
  618. ASSERT_FALSE(PgSqlExchange::isColumnNull(*r, row, TEXT_COL));
  619. // Fetch and verify the column value
  620. fetched_str = "";
  621. ASSERT_NO_THROW(PgSqlExchange::getColumnValue(*r, row, TEXT_COL,
  622. fetched_str));
  623. EXPECT_EQ(fetched_str, ref_string);
  624. }
  625. // While we here, verify that bad row throws
  626. ASSERT_THROW(PgSqlExchange::getColumnValue(*r, row, TEXT_COL, fetched_str),
  627. DbOperationError);
  628. // Clean out the table
  629. WIPE_ROWS(r);
  630. // Verify we can insert a NULL value.
  631. bind_array.reset(new PsqlBindArray());
  632. bind_array->addNull();
  633. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  634. // Fetch the newly inserted row.
  635. FETCH_ROWS(r, 1);
  636. // Verify the column is null.
  637. ASSERT_TRUE(PgSqlExchange::isColumnNull(*r, 0, TEXT_COL));
  638. }
  639. /// @brief Verify that we can read and write VARCHAR columns
  640. TEST_F(PgSqlBasicsTest, varcharTest) {
  641. // Create a prepared statement for inserting a VARCHAR
  642. PgSqlTaggedStatement statement[] = {
  643. { 1, { OID_VARCHAR }, "varchar_insert",
  644. "INSERT INTO BASICS (varchar_col) values ($1)" }
  645. };
  646. ASSERT_NO_THROW(conn_->prepareStatement(statement[0]));
  647. // Our reference string.
  648. std::string ref_string = "This is a varchar string";
  649. // Insert the reference from std::string
  650. PsqlBindArrayPtr bind_array;
  651. PgSqlResultPtr r;
  652. bind_array.reset(new PsqlBindArray());
  653. bind_array->add(ref_string);
  654. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  655. // Insert the reference from a buffer
  656. bind_array.reset(new PsqlBindArray());
  657. bind_array->add(ref_string.c_str());
  658. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  659. // Fetch the newly inserted rows.
  660. FETCH_ROWS(r, 2);
  661. // Iterate over the rows, verifying the value against the reference
  662. std::string fetched_str;
  663. int row = 0;
  664. for ( ; row < 2; ++row ) {
  665. // Verify the column is not null.
  666. ASSERT_FALSE(PgSqlExchange::isColumnNull(*r, row, VARCHAR_COL));
  667. // Fetch and verify the column value
  668. fetched_str = "";
  669. ASSERT_NO_THROW(PgSqlExchange::getColumnValue(*r, row, VARCHAR_COL,
  670. fetched_str));
  671. EXPECT_EQ(fetched_str, ref_string);
  672. }
  673. // While we here, verify that bad row throws
  674. ASSERT_THROW(PgSqlExchange::getColumnValue(*r, row, VARCHAR_COL,
  675. fetched_str), DbOperationError);
  676. // Clean out the table
  677. WIPE_ROWS(r);
  678. // Verify we can insert a NULL value.
  679. bind_array.reset(new PsqlBindArray());
  680. bind_array->addNull();
  681. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  682. // Fetch the newly inserted rows
  683. FETCH_ROWS(r, 1);
  684. // Verify the column is null.
  685. ASSERT_TRUE(PgSqlExchange::isColumnNull(*r, 0, VARCHAR_COL));
  686. }
  687. /// @brief Verify that we can read and write TIMESTAMP columns
  688. TEST_F(PgSqlBasicsTest, timeStampTest) {
  689. // Create a prepared statement for inserting a TIMESTAMP
  690. PgSqlTaggedStatement statement[] = {
  691. { 1, { OID_TIMESTAMP }, "timestamp_insert",
  692. "INSERT INTO BASICS (timestamp_col) values ($1)" }
  693. };
  694. ASSERT_NO_THROW(conn_->prepareStatement(statement[0]));
  695. // Build our list of reference times
  696. time_t now;
  697. time(&now);
  698. std::vector<time_t> times;
  699. times.push_back(now);
  700. times.push_back(DatabaseConnection::MAX_DB_TIME);
  701. // Note on a 32-bit OS this value is really -1. PosgreSQL will store it
  702. // and return it intact.
  703. times.push_back(0xFFFFFFFF);
  704. // Insert a row for each reference value
  705. PsqlBindArrayPtr bind_array;
  706. PgSqlResultPtr r;
  707. std::string time_str;
  708. for (int i = 0; i < times.size(); ++i) {
  709. // Timestamps are inserted as strings so convert them first
  710. ASSERT_NO_THROW(time_str =
  711. PgSqlExchange::convertToDatabaseTime(times[i]));
  712. // Add it to the bind array and insert it
  713. bind_array.reset(new PsqlBindArray());
  714. bind_array->add(time_str);
  715. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  716. }
  717. // Insert a row with ref time plus one day
  718. times.push_back(now + 24*3600);
  719. ASSERT_NO_THROW(time_str =
  720. PgSqlExchange::convertToDatabaseTime(times[0], 24*3600));
  721. bind_array.reset(new PsqlBindArray());
  722. bind_array->add(time_str);
  723. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  724. // Fetch the newly inserted rows.
  725. FETCH_ROWS(r, times.size());
  726. // Iterate over the rows, verifying the value against its reference
  727. std::string fetched_str;
  728. int row = 0;
  729. for ( ; row < times.size(); ++row ) {
  730. // Verify the column is not null.
  731. ASSERT_FALSE(PgSqlExchange::isColumnNull(*r, row, TIMESTAMP_COL));
  732. // Fetch and verify the column value
  733. fetched_str = "";
  734. ASSERT_NO_THROW(PgSqlExchange::getColumnValue(*r, row, TIMESTAMP_COL,
  735. fetched_str));
  736. time_t fetched_time;
  737. ASSERT_NO_THROW(fetched_time =
  738. PgSqlExchange::convertFromDatabaseTime(fetched_str));
  739. EXPECT_EQ(fetched_time, times[row]) << " row: " << row;
  740. }
  741. // While we here, verify that bad row throws
  742. ASSERT_THROW(PgSqlExchange::getColumnValue(*r, row, TIMESTAMP_COL,
  743. fetched_str), DbOperationError);
  744. // Clean out the table
  745. WIPE_ROWS(r);
  746. // Verify we can insert a NULL value.
  747. bind_array.reset(new PsqlBindArray());
  748. bind_array->addNull();
  749. RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
  750. // Fetch the newly inserted rows
  751. FETCH_ROWS(r, 1);
  752. // Verify the column is null.
  753. ASSERT_TRUE(PgSqlExchange::isColumnNull(*r, 0, TIMESTAMP_COL));
  754. // Verify exceeding max time throws
  755. ASSERT_THROW(PgSqlExchange::convertToDatabaseTime(times[0],
  756. DatabaseConnection::
  757. MAX_DB_TIME), BadValue);
  758. }
  759. }; // namespace