}
void PsqlBindArray::insert(const char* value, size_t index) {
- if (index >= values_.size()) {
- isc_throw(OutOfRange, "PsqlBindArray::insert - index: " << index
+ if (index && index >= values_.size()) {
+ isc_throw(OutOfRange, "PsqlBindArray::insert - index: " << index
<< ", is larger than the array size: " << values_.size());
}
void PsqlBindArray::insert(const std::string& value, size_t index) {
if (index >= values_.size()) {
- isc_throw(OutOfRange, "PsqlBindArray::insert - index: " << index
+ isc_throw(OutOfRange, "PsqlBindArray::insert - index: " << index
<< ", is larger than the array size: " << values_.size());
}
}
void
-PsqlBindArray::addOptionalIPv4Address(const util::Optional<isc::asiolink::IOAddress>& value) {
+PsqlBindArray::addInet4(const isc::asiolink::IOAddress& value) {
+ if (!value.isV4()) {
+ isc_throw(BadValue, "unable to add address to PsqlBindAray '"
+ << value.toText() << "' is not an IPv4 address");
+ }
+
+ // inet columns are inserted as string addresses.
+ addTempString(value.toText());
+}
+
+void
+PsqlBindArray::addOptionalInet4(const util::Optional<isc::asiolink::IOAddress>& value) {
// If the value is unspecified it doesn't matter what the value is.
if (value.unspecified()) {
addNull();
} else {
- // Make sure it is an IPv4 address.
- if (!value.get().isV4()) {
- isc_throw(BadValue, "unable to add address to PsqlBindAray '"
- << value.get().toText() << "' is not an IPv4 address");
- }
+ addInet4(value);
+ }
+}
+
+void
+PsqlBindArray::addInet6(const isc::asiolink::IOAddress& value) {
+ if (!value.isV6()) {
+ isc_throw(BadValue, "unable to add address to PsqlBindAray '"
+ << value.toText() << "' is not an IPv6 address");
+ }
- add((value.get().toUint32()));
+ // inet columns are inserted as string addresses.
+ addTempString(value.toText());
+}
+
+void
+PsqlBindArray::addOptionalInet6(const util::Optional<isc::asiolink::IOAddress>& value) {
+ // If the value is unspecified it doesn't matter what the value is.
+ if (value.unspecified()) {
+ addNull();
+ } else {
+ addInet6(value);
}
}
addTempString(PgSqlExchange::convertToDatabaseTime(now));
}
+
std::string
PsqlBindArray::toText() const {
std::ostringstream stream;
PgSqlExchange::convertFromDatabaseTime(db_time_val, value);
}
+isc::asiolink::IOAddress
+PgSqlExchange::getInetValue4(const PgSqlResult& r, const int row,
+ const size_t col) {
+ const char* data = getRawColumnValue(r, row, col);
+ try {
+ asiolink::IOAddress addr(data);
+ if (!addr.isV4()) {
+ isc_throw(BadValue, "not a v4 address");
+ }
+
+ return(addr);
+ } catch (const std::exception& ex) {
+ isc_throw(DbOperationError, "Cannot convert data: " << data
+ << " for: " << getColumnLabel(r, col) << " row:" << row
+ << " : " << ex.what());
+ }
+}
+
+
+isc::asiolink::IOAddress
+PgSqlExchange::getInetValue6(const PgSqlResult& r, const int row,
+ const size_t col) {
+ const char* data = getRawColumnValue(r, row, col);
+ try {
+ asiolink::IOAddress addr(data);
+ if (!addr.isV6()) {
+ isc_throw(BadValue, "not a v6 address");
+ }
+
+ return(addr);
+ } catch (const std::exception& ex) {
+ isc_throw(DbOperationError, "Cannot convert data: " << data
+ << " for: " << getColumnLabel(r, col) << " row:" << row
+ << " : " << ex.what());
+ }
+}
+
isc::asiolink::IOAddress
PgSqlExchange::getIPv6Value(const PgSqlResult& r, const int row,
const size_t col) {
}
}
+ /// @brief Adds an IPv4 address to the bind array.
+ ///
+ /// This is used for inet type columns.
+ ///
+ /// @param value Optional boolean value to add
+ /// @throw BadValue if the address is not a IPv4 address.
+ void addInet4(const isc::asiolink::IOAddress& value);
+
+ /// @brief Adds an IPv6 address to the bind array.
+ ///
+ /// This is used for inet type columns.
+ ///
+ /// @param value Optional boolean value to add
+ /// @throw BadValue if the address is not a IPv6 address.
+ void addInet6(const isc::asiolink::IOAddress& value);
+
/// @brief Adds an @c Optional IPv4 address to the bind array.
///
+ /// This is used for inet type columns.
+ ///
/// @param value Optional boolean value to add
/// @throw BadValue if the address is not a IPv4 address.
- void addOptionalIPv4Address(const util::Optional<isc::asiolink::IOAddress>& value);
+ void addOptionalInet4(const util::Optional<isc::asiolink::IOAddress>& value);
+
+ /// @brief Adds an @c Optional IPv6 address to the bind array.
+ ///
+ /// This is used for inet type columns which expect
+ /// v4 addresses to be inserted in string form:
+ /// '3001::1'
+ ///
+ /// @param value Optional boolean value to add
+ /// @throw BadValue if the address is not a IPv6 address.
+ void addOptionalInet6(const util::Optional<isc::asiolink::IOAddress>& value);
+
/// @brief Adds a timestamp from a ptime to the bind array.
///
static void getColumnValue(const PgSqlResult& r, const int row,
const size_t col, uint8_t &value);
+ /// @brief Converts a column in a row in a result set into IPv4 address.
+ ///
+ /// This is used to fetch values from inet type columns.
+ ///
+ /// @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
+ ///
+ /// @return isc::asiolink::IOAddress containing the IPv4 address.
+ /// @throw DbOperationError if the value cannot be fetched or is
+ /// invalid.
+ static isc::asiolink::IOAddress getInetValue4(const PgSqlResult& r,
+ const int row,
+ const size_t col);
+
+ /// @brief Converts a column in a row in a result set into IPv6 address.
+ ///
+ /// This is used to fetch values from inet type columns.
+ ///
+ /// @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
+ ///
+ /// @return isc::asiolink::IOAddress containing the IPv6 address.
+ /// @throw DbOperationError if the value cannot be fetched or is
+ /// invalid.
+ static isc::asiolink::IOAddress getInetValue6(const PgSqlResult& r,
+ const int row,
+ const size_t col);
+
/// @brief Converts a column in a row in a result set into IPv6 address.
///
+ /// This used for IPv6 columns stored as varchar.
+ ///
/// @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
/// @brief Verifies the ability to add Optional IPv4 addresses to
/// the bind array.
-TEST(PsqlBindArray, addOptionalIPv4Address) {
+TEST(PsqlBindArray, addOptionalInet4) {
PsqlBindArray b;
// Verify we cannot add a v6 address.
Optional<asiolink::IOAddress> not_v4(asiolink::IOAddress("3001::1"));
- ASSERT_THROW_MSG(b.addOptionalIPv4Address(not_v4), BadValue,
+ ASSERT_THROW_MSG(b.addOptionalInet4(not_v4), BadValue,
"unable to add address to PsqlBindAray"
" '3001::1' is not an IPv4 address");
- // Add the addresses to the array..
- b.addOptionalInteger(empty);
- b.addOptionalInteger(not_empty);
+ // Add addresses to bind array.
+ b.addOptionalInet4(empty);
+ b.addOptionalInet4(not_empty);
}
// We've left bind scope, everything should be intact.
EXPECT_EQ(expected, b.toText());
}
+/// @brief Verifies the ability to add Optional IPv6 addresses to
+/// the bind array.
+TEST(PsqlBindArray, addOptionalInet6) {
+
+ PsqlBindArray b;
+
+ // Add all the items within a different scope. Everything should
+ // still be valid once we exit this scope.
+ {
+ Optional<asiolink::IOAddress> empty;
+ Optional<asiolink::IOAddress> not_empty(asiolink::IOAddress("3001::1"));
+
+ // Verify we cannot add a v6 address.
+ Optional<asiolink::IOAddress> not_v6(asiolink::IOAddress("192.168.1.1"));
+ ASSERT_THROW_MSG(b.addOptionalInet6(not_v6), BadValue,
+ "unable to add address to PsqlBindAray"
+ " '192.168.1.1' is not an IPv6 address");
+
+ // Add addresses to bind array.
+ b.addOptionalInet6(empty);
+ b.addOptionalInet6(not_empty);
+ }
+
+ // We've left bind scope, everything should be intact.
+ EXPECT_EQ(2, b.size());
+
+ // Verify contents are correct.
+ std::string expected =
+ "0 : empty\n"
+ "1 : \"3001::1\"\n";
+
+ EXPECT_EQ(expected, b.toText());
+}
+
/// @brief Verifies that PgResultSet row and column meta-data is correct
TEST_F(PgSqlBasicsTest, rowColumnBasics) {
// We fetch the table contents, which at this point should be no rows.
TEST(PsqlBindArray, insertString) {
PsqlBindArray b;
- // Make not temporary strings to insert.
+ // Make a non-temporary string to insert.
std::string one("one");
-// std::string three("three");
// Add all the items within a different scope. Everything should
// still be valid once we exit this scope.
{
- b.add("two");
+ // Make sure you can "insert" at the front of an empty array.
+ b.insert("two", 0);
+
+ // Add a binding.
b.add("four");
- ASSERT_THROW_MSG(b.insert(std::string("too far"), 5), OutOfRange,
+ // Verify an out of range index throws.
+ ASSERT_THROW_MSG(b.insert(std::string("too far"), 5), OutOfRange,
"PsqlBindArray::insert - index: 5, "
"is larger than the array size: 2");
+
+ // Insert a non-temporary string at the front.
b.insert(one, 0);
+
+ // Insert a temporary string.
b.insert("three", 2);
+
+ // Add another one.
b.add("five");
}
EXPECT_EQ(expected, b.toText());
}
+/// @brief Verify that we can read and write IPv4 addresses
+/// using INET columns.
+TEST_F(PgSqlBasicsTest, inetTest4) {
+ // Create a prepared statement for inserting a SMALLINT
+ const char* st_name = "smallint_insert";
+ PgSqlTaggedStatement statement[] = {
+ { 1, { OID_TEXT }, st_name,
+ "INSERT INTO BASICS (inet_col) values (cast($1 as inet))" }
+ };
+
+ ASSERT_NO_THROW(conn_->prepareStatement(statement[0]));
+
+ // Build our reference list of reference values
+ std::vector<asiolink::IOAddress>inets;
+ inets.push_back(asiolink::IOAddress("0.0.0.0"));
+ inets.push_back(asiolink::IOAddress("192.168.1.1"));
+
+ // Insert a row for each reference value
+ PsqlBindArrayPtr bind_array;
+ PgSqlResultPtr r;
+ for (int i = 0; i < inets.size(); ++i) {
+ bind_array.reset(new PsqlBindArray());
+ bind_array->addInet4(inets[i]);
+ RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
+ }
+
+ // Fetch the newly inserted rows.
+ FETCH_ROWS(r, inets.size());
+
+ // Iterate over the rows, verifying each value against its reference
+ int row = 0;
+ for ( ; row < inets.size(); ++row ) {
+ // Verify the column is not null.
+ ASSERT_FALSE(PgSqlExchange::isColumnNull(*r, row, INET_COL));
+
+ // Fetch and verify the column value
+ asiolink::IOAddress fetched_inet("0.0.0.0");
+ ASSERT_NO_THROW(fetched_inet = PgSqlExchange::getInetValue4(*r, row, INET_COL));
+ EXPECT_EQ(fetched_inet, inets[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, INET_COL));
+}
+
+/// @brief Verify that we can read and write IPv6 addresses
+/// using INET columns.
+TEST_F(PgSqlBasicsTest, inetTest6) {
+ // Create a prepared statement for inserting a SMALLINT
+ const char* st_name = "smallint_insert";
+ PgSqlTaggedStatement statement[] = {
+ { 1, { OID_TEXT }, st_name,
+ "INSERT INTO BASICS (inet_col) values (cast($1 as inet))" }
+ };
+
+ ASSERT_NO_THROW(conn_->prepareStatement(statement[0]));
+
+ // Build our reference list of reference values
+ std::vector<asiolink::IOAddress>inets;
+ inets.push_back(asiolink::IOAddress("::"));
+ inets.push_back(asiolink::IOAddress("3001::1"));
+
+ // Insert a row for each reference value
+ PsqlBindArrayPtr bind_array;
+ PgSqlResultPtr r;
+ for (int i = 0; i < inets.size(); ++i) {
+ bind_array.reset(new PsqlBindArray());
+ bind_array->addInet6(inets[i]);
+ RUN_PREP(r, statement[0], bind_array, PGRES_COMMAND_OK);
+ }
+
+ // Fetch the newly inserted rows.
+ FETCH_ROWS(r, inets.size());
+
+ // Iterate over the rows, verifying each value against its reference
+ int row = 0;
+ for ( ; row < inets.size(); ++row ) {
+ // Verify the column is not null.
+ ASSERT_FALSE(PgSqlExchange::isColumnNull(*r, row, INET_COL));
+
+ // Fetch and verify the column value
+ asiolink::IOAddress fetched_inet("::");
+ ASSERT_NO_THROW(fetched_inet = PgSqlExchange::getInetValue6(*r, row, INET_COL));
+ EXPECT_EQ(fetched_inet, inets[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, INET_COL));
+}
+
+
+
}; // namespace