#include <vector>
using namespace isc::util;
+using namespace isc::data;
namespace isc {
namespace db {
addTempString(PgSqlExchange::convertToDatabaseTime(now));
}
+void
+PsqlBindArray::add(const ElementPtr& value) {
+ if (!value) {
+ addNull();
+ return;
+ }
+
+ std::ostringstream ss;
+ value->toJSON(ss);
+ addTempString(ss.str());
+}
std::string
PsqlBindArray::toText() const {
PgSqlExchange::convertFromDatabaseTime(db_time_val, value);
}
+void
+PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
+ const size_t col, ElementPtr& value) {
+ const char* data = getRawColumnValue(r, row, col);
+ try {
+ value = Element::fromJSON(data);
+ } catch (const std::exception& ex) {
+ isc_throw(DbOperationError, "Cannot convert data: " << data
+ << " for: " << getColumnLabel(r, col) << " row:" << row
+ << " : " << ex.what());
+ }
+}
+
isc::asiolink::IOAddress
PgSqlExchange::getInetValue4(const PgSqlResult& r, const int row,
const size_t col) {
#include <asiolink/io_address.h>
#include <database/database_connection.h>
+#include <cc/data.h>
#include <util/triplet.h>
#include <util/boost_time_utils.h>
#include <exceptions/exceptions.h>
/// Precision is seconds.
void addTimestamp();
+ /// @brief Adds an ElementPtr to the bind array
+ ///
+ /// Adds a TEXT_FMT value to the end of the bind array containing
+ /// the JSON text output by given ElementPtr::toJSON().
+ ///
+ /// @param value ElementPtr containing Element tree to add.
+ /// @throw DbOperationError if value is NULL.
+ void add(const data::ElementPtr& value);
+
/// @brief Dumps the contents of the array to a string.
/// @return std::string containing the dump
std::string toText() const;
static void getColumnValue(const PgSqlResult& r, const int row,
const size_t col, boost::posix_time::ptime& value);
+ /// @brief Fetches a JSON column as an ElementPtr.
+ ///
+ /// @param r the result set containing the query results
+ /// @param row the row number within the result set
+ /// @param col the column number within the row
+ ///
+ /// @throw DbOperationError if the value cannot be fetched or is
+ /// invalid.
+ static void getColumnValue(const PgSqlResult& r, const int row,
+ const size_t col, data::ElementPtr& value);
+
/// @brief Converts a column in a row in a result set to a binary bytes
///
/// Method is used to convert columns stored as BYTEA into a buffer of
VARCHAR_COL,
INET_COL,
FLOAT_COL,
+ JSON_COL,
NUM_BASIC_COLS
};
/// @brief Constructor
expectedColNames_[VARCHAR_COL] = "varchar_col";
expectedColNames_[INET_COL] = "inet_col";
expectedColNames_[FLOAT_COL] = "float_col";
+ expectedColNames_[JSON_COL] = "json_col";
destroySchema();
createSchema();
" timestamp_col TIMESTAMP WITH TIME ZONE, "
" varchar_col VARCHAR(255), "
" inet_col INET, "
- " float_col FLOAT "
+ " float_col FLOAT, "
+ " json_col JSON "
"); ";
PgSqlResult r(PQexec(*conn_, sql));
" id, bool_col, bytea_col, bigint_col, smallint_col, "
" int_col, text_col,"
" extract(epoch from timestamp_col)::bigint as timestamp_col,"
- " varchar_col, inet_col, float_col"
+ " varchar_col, inet_col, float_col, json_col"
" FROM basics";
runSql(r, sql, PGRES_TUPLES_OK, line);
#include <vector>
using namespace isc;
+using namespace isc::data;
using namespace isc::db;
using namespace isc::db::test;
using namespace isc::util;
// Add an empty string.
b.add(std::string(""));
+
+ // Add a v4 address.
+ asiolink::IOAddress addr4("192.168.1.1");
+ b.addInet4(addr4);
+
+ // Add a v6 address.
+ asiolink::IOAddress addr6("3001::1");
+ b.addInet6(addr6);
+
+ // Add a double. Not sure how portably reliable this test will be.
+ double dbl = 2.0;
+ b.add(dbl);
+
+ // Add a JSON.
+ ElementPtr elems = Element::fromJSON("{ \"foo\": \"bar\" }");
+ b.add(elems);
}
// We've left bind scope, everything should be intact.
"8 : \"3221360418\"\n"
"9 : \"3001::1\"\n"
"10 : 0x0102030405060708090a\n"
- "11 : empty\n";
+ "11 : empty\n"
+ "12 : \"192.168.1.1\"\n"
+ "13 : \"3001::1\"\n"
+ "14 : \"2\"\n"
+ "15 : \"{ \"foo\": \"bar\" }\"\n";
EXPECT_EQ(expected, b.toText());
}
EXPECT_EQ(expected, b.toText());
}
-
/// @brief Verifies the ability to add Optional booleans to
/// the bind array.
TEST(PsqlBindArray, addOptionalBool) {
ASSERT_TRUE(PgSqlExchange::isColumnNull(*r, 0, FLOAT_COL));
}
+/// @brief Verify that we can read and write JSON columns.
+TEST_F(PgSqlBasicsTest, jsonTest) {
+ // Create a prepared statement for inserting a SMALLINT
+ const char* st_name = "json_insert";
+ PgSqlTaggedStatement statement[] = {
+ { 1, { OID_TEXT }, st_name,
+ "INSERT INTO BASICS (json_col) values (cast($1 as json))" }
+ };
+
+ ASSERT_NO_THROW_LOG(conn_->prepareStatement(statement[0]));
+
+ // Build our reference list of reference values
+ std::vector<ElementPtr>elem_ptrs;
+ ASSERT_NO_THROW_LOG(elem_ptrs.push_back(Element::fromJSON("{ \"one\":1 }")));
+ ASSERT_NO_THROW_LOG(elem_ptrs.push_back(Element::fromJSON("{ \"two\":\"two\" }")));
+
+ // Insert a row for each reference value
+ PsqlBindArrayPtr bind_array;
+ PgSqlResultPtr r;
+ for (int i = 0; i < elem_ptrs.size(); ++i) {
+ bind_array.reset(new PsqlBindArray());
+ bind_array->add(elem_ptrs[i]);
+ RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
+ }
+
+ // Fetch the newly inserted rows.
+ FETCH_ROWS(r, elem_ptrs.size());
+
+ // Iterate over the rows, verifying each value against its reference
+ int row = 0;
+ for ( ; row < elem_ptrs.size(); ++row ) {
+ // Verify the column is not null.
+ ASSERT_FALSE(PgSqlExchange::isColumnNull(*r, row, JSON_COL));
+
+ // Fetch and verify the column value
+ ElementPtr fetched_ptr;
+ ASSERT_NO_THROW_LOG(PgSqlExchange::getColumnValue(*r, row, JSON_COL, fetched_ptr));
+ EXPECT_TRUE(fetched_ptr->equals(*(elem_ptrs[row])));
+ }
+
+ // Clean out the table
+ WIPE_ROWS(r);
+
+ // Verify we can insert a NULL value.
+ bind_array.reset(new PsqlBindArray());
+ bind_array->addNull();
+ RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
+
+ // Fetch the newly inserted row.
+ FETCH_ROWS(r, 1);
+
+ // Verify the column is null.
+ ASSERT_TRUE(PgSqlExchange::isColumnNull(*r, 0, JSON_COL));
+}
+
}; // namespace