Browse Source

[3382] v4 Working with new PsqlBindArray

Initial use of PsqlBindArray, working for v4.
Thomas Markwalder 11 years ago
parent
commit
179876c5e6
2 changed files with 338 additions and 104 deletions
  1. 307 104
      src/lib/dhcpsrv/pgsql_lease_mgr.cc
  2. 31 0
      src/lib/dhcpsrv/pgsql_lease_mgr.h

+ 307 - 104
src/lib/dhcpsrv/pgsql_lease_mgr.cc

@@ -62,6 +62,16 @@ struct TaggedStatement {
     const char* text;
 };
 
+#if 0
+const OID_BOOL = 16;
+const OID_BTYEA = 17;
+const OID_INT8 = 20;  // 8 byte int
+const OID_INT4 = 23;  // 8 byte int
+const OID_INT2 = 21;  // 2 byte int
+const OID_TIMESTAMP = 1114;
+const OID_VARCHAR = 1043;  // ? what's diff between this and OID/25 = text
+#endif
+
 TaggedStatement tagged_statements[] = {
     {PgSqlLeaseMgr::DELETE_LEASE4, 1,
         { 20 },
@@ -172,9 +182,46 @@ TaggedStatement tagged_statements[] = {
 namespace isc {
 namespace dhcp {
 
+const int PsqlBindArray::TEXT_FMT = 0;
+const int PsqlBindArray::BINARY_FMT = 1;
+const char* PsqlBindArray::TRUE_STR = "TRUE";
+const char* PsqlBindArray::FALSE_STR = "FALSE";
+
+void PsqlBindArray::add(const char* value) {
+//        std::cout << "adding char* : " << value << std::endl;
+    values_.push_back(value);
+    lengths_.push_back(strlen(value));
+    formats_.push_back(TEXT_FMT);
+}
+
+void PsqlBindArray::add(const std::string& value) {
+//        std::cout << "adding text : " << value << std::endl;
+    values_.push_back(value.c_str());
+    lengths_.push_back(value.size());
+    formats_.push_back(TEXT_FMT);
+}
+
+void PsqlBindArray::add(const std::vector<uint8_t>& data) {
+//        std::cout << "adding bin" << std::endl;
+    values_.push_back(reinterpret_cast<const char*>(&(data[0])));
+    lengths_.push_back(data.size());
+    formats_.push_back(BINARY_FMT);
+}
+
+void PsqlBindArray::add(const uint32_t& value)  {
+//        std::cout << "adding 32int bin" << std::endl;
+    values_.push_back(reinterpret_cast<const char*>(&value));
+    lengths_.push_back(sizeof(uint32_t));
+    formats_.push_back(BINARY_FMT);
+}
+
+void PsqlBindArray::add(const bool& value)  {
+    add(value ? TRUE_STR : FALSE_STR);
+}
+
 /// @brief Auxiliary PostgreSQL exchange class
 class PgSqlLeaseExchange {
-protected:
+public:
 
     /// @brief Converts time_t structure to a text representation in local time.
     ///
@@ -218,7 +265,7 @@ protected:
     //  Any other will throw.
     /// @param value text value to be converted
     /// @throw BadValue if passed any value other than "t" or "f"
-    bool stringToBool(char* value) {
+    bool stringToBool(const char* value) {
         if (!value || !strlen(value)) {
             return (false);
         }
@@ -228,18 +275,114 @@ protected:
         case 'f':
             return (false);
         default:
-            isc_throw(BadValue, "Received " << value[0] << " as boolean. The "
-                      " only accepted values are 't', 'f' or ''");
+            isc_throw(BadValue, "invalid bool value: " << value); 
+        }
+    }
+
+    void getColumnValue(PGresult* &r, const int row, const size_t col,
+                        bool &value) {
+        const char* data = getColumnValue(r, row, col);
+        if (!strlen(data) || *data == 'f') {
+            value = false;
+        } else if (*data == 't') {
+            value = true;
+        } else {
+            isc_throw(DbOperationError, "Cannot convert data: " << data 
+                      << " for: " << getColumnLabel(col) << " row:" << row);
+        }
+    }
+
+    void getColumnValue(PGresult* &r, const int row, const size_t col,
+                        uint32_t &value) {
+        const char* data = getColumnValue(r, row, col);
+        try {
+            value = boost::lexical_cast<uint32_t>(data);
+        } catch (const std::exception& ex) {
+            isc_throw(DbOperationError, "Cannot convert data: " << data 
+                      << " for: " << getColumnLabel(col) << " row:" << row 
+                      << " : " << ex.what());
         }
     }
 
+    const char* getColumnValue(PGresult* &r, const int row,
+                                        const size_t col) {
+        const char* value = PQgetvalue(r, row, col);
+        if (!value) {
+            isc_throw(DbOperationError, "getColumnValue no data for :"
+                      << getColumnLabel(col) << " row:" << row);
+        } 
+
+        return (value);
+    }
+
+    void convertFromBytea(PGresult* &r, const int row, const size_t col,
+                          uint8_t* dest,
+                          const size_t max, size_t &bytes_converted) {
+
+        unsigned char* bytes 
+            = PQunescapeBytea((const unsigned char*)
+                              (getColumnValue(r, row, col)), &bytes_converted);
+        if (!bytes) {
+            isc_throw (DbOperationError, "PQunescapeBytea failed for:"
+                       << getColumnLabel(col) << " row:" << row);
+        }
+
+        if (bytes_converted > max) {
+            PQfreemem(bytes);
+            isc_throw (DbOperationError, "Converted data size: " 
+                       << bytes_converted << " is too large for: " 
+                       << getColumnLabel(col) << " row:" << row);
+        }
+
+        memcpy(dest, bytes, bytes_converted);
+        PQfreemem(bytes);
+    }
+
+    std::string toHexText(const char* data, size_t len) {
+        std::ostringstream stream;
+        stream << "Data length is: " << len << std::endl;
+        for (int i = 0; i < len; ++i) {
+            if (i > 0 && ((i % 16) == 0)) {
+                stream << std::endl;
+            }
+
+            stream << setfill('0') << setw(2) << setbase(16)
+                   << static_cast<unsigned int>(data[i]) << " ";
+        }
+
+        return (stream.str());
+    }
+
     /// Compiled statement bind parameters
     BindParams params;
+
+    std::vector<std::string>columnLabels_;
+
+    std::string getColumnLabel(const size_t index) {
+        if (index > columnLabels_.size()) {
+            ostringstream os;
+            os << "Unknown column:" << index;
+            return (os.str());
+        }
+
+        return (columnLabels_[index]);
+    }
 };
 
+
 /// @brief Represents a single Lease4 exchange
 class PgSqlLease4Exchange : public PgSqlLeaseExchange {
 private:
+
+    static const size_t ADDRESS_COL = 0;
+    static const size_t HWADDR_COL = 1;
+    static const size_t CLIENT_ID_COL = 2;
+    static const size_t VALID_LIFETIME_COL = 3;
+    static const size_t EXPIRE_COL = 4;
+    static const size_t SUBNET_ID_COL = 5;
+    static const size_t FQDN_FWD_COL = 6;
+    static const size_t FQDN_REV_COL = 7;
+    static const size_t HOSTNAME_COL = 8;
     /// @brief Number of columns in the table holding DHCPv4 leases.
     static const size_t LEASE_COLUMNS = 9;
 
@@ -247,125 +390,102 @@ public:
 
     /// @brief Default constructor
     PgSqlLease4Exchange() : addr4_(0), hwaddr_length_(0), client_id_length_(0) {
+
+        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
+
         memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
         memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
 
         // Set the column names (for error messages)
-        columns_[0] = "address";
-        columns_[1] = "hwaddr";
-        columns_[2] = "client_id";
-        columns_[3] = "valid_lifetime";
-        columns_[4] = "expire";
-        columns_[5] = "subnet_id";
-        columns_[6] = "fqdn_fwd";
-        columns_[7] = "fqdn_rev";
-        columns_[8] = "hostname";
-
-        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
+        /// @todo something more elegant than this
+        columnLabels_.push_back("address");
+        columnLabels_.push_back("hwaddr");
+        columnLabels_.push_back("client_id");
+        columnLabels_.push_back("valid_lifetime");
+        columnLabels_.push_back("expire");
+        columnLabels_.push_back("subnet_id");
+        columnLabels_.push_back("fqdn_fwd");
+        columnLabels_.push_back("fqdn_rev");
+        columnLabels_.push_back("hostname");
 
         params.reserve(LEASE_COLUMNS);
     }
 
-    BindParams
-    createBindForSend(const Lease4Ptr& lease) {
+    void
+    createBindForSend(const Lease4Ptr& lease, PsqlBindArray& bind_array) {
+        // Store lease object to ensure it remains valid.
         lease_ = lease;
-        params.clear();
-        ostringstream tmp;
 
-        tmp << static_cast<uint32_t>(lease_->addr_);
-        params.push_back(PgSqlParam(tmp.str()));
-        tmp.str("");
+        addr4_str_ = boost::lexical_cast<std::string>
+                     (static_cast<uint32_t>(lease->addr_));
+
+        bind_array.add(addr4_str_);
 
         // Although HWADDR object will always be there, it may be just an
         // empty vector
-        if (!lease_->hwaddr_.empty()) {
+        if (!lease->hwaddr_.empty()) {
             if (lease->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
-                isc_throw(DbOperationError,
-                          "Hardware address length : "
+                isc_throw(DbOperationError, "Hardware address length : "
                           << lease->hwaddr_.size()
                           << " exceeds maximum allowed of: "
                           << HWAddr::MAX_HWADDR_LEN);
             }
 
-            params.push_back(PgSqlParam(lease_->hwaddr_));
+            bind_array.add(lease->hwaddr_);
         } else {
-            params.push_back(PgSqlParam());
+            bind_array.add("");
         }
-
-        if (lease_->client_id_) {
-            params.push_back(PgSqlParam(lease_->client_id_->getClientId()));
+    
+        if (lease->client_id_) {
+            bind_array.add(lease->client_id_->getClientId());
         } else {
-            params.push_back(PgSqlParam());
+            bind_array.add("");
         }
 
-        tmp << static_cast<unsigned long>(lease_->valid_lft_);
-        params.push_back(PgSqlParam(tmp.str()));
-        tmp.str("");
+        valid_lft_str_ = boost::lexical_cast<std::string>(lease->valid_lft_);
+        bind_array.add(valid_lft_str_);
 
-        time_t expire = lease_->valid_lft_ + lease_->cltt_;
-        params.push_back(PgSqlParam(convertToDatabaseTime(expire)));
+        expire_str_ = convertToDatabaseTime(lease->valid_lft_ + lease->cltt_);
+        bind_array.add(expire_str_);
 
-        tmp << static_cast<unsigned long>(lease_->subnet_id_);
-        params.push_back(PgSqlParam(tmp.str()));
+        subnet_id_str_ = boost::lexical_cast<std::string>(lease->subnet_id_);
+        bind_array.add(subnet_id_str_);
 
-        params.push_back(PgSqlParam(lease_->fqdn_fwd_ ? "TRUE" : "FALSE"));
-        params.push_back(PgSqlParam(lease_->fqdn_rev_ ? "TRUE" : "FALSE"));
-        params.push_back(PgSqlParam(lease_->hostname_));
+        bind_array.add(lease->fqdn_fwd_);
+        bind_array.add(lease->fqdn_rev_);
 
-        return (params);
+        bind_array.add(lease->hostname_);
     }
 
     Lease4Ptr
-    convertFromDatabase(PGresult *& r, int line) {
-        const char* addr4_str = PQgetvalue(r, line, 0);
-        unsigned char* hwaddr_str = PQunescapeBytea((const unsigned char *)
-                                                    PQgetvalue(r, line, 1),
-                                                    & hwaddr_length_);
-        unsigned char* client_id_str = PQunescapeBytea((const unsigned char *)
-                                                       PQgetvalue(r, line, 2),
-                                                       & client_id_length_);
-        const char* valid_lifetime_str = PQgetvalue(r, line, 3);
-        const char* expire_str = PQgetvalue(r, line, 4);
-        const char* subnet_id_str = PQgetvalue(r, line, 5);
-        unsigned long valid_lifetime;
-        unsigned long subnet_id;
+    convertFromDatabase(PGresult *& r, int row) {
 
-        istringstream tmp;
-        tmp.str(addr4_str);
-        tmp >> addr4_;
-        tmp.clear();
+        getColumnValue(r, row, ADDRESS_COL, addr4_);
 
-        memcpy(hwaddr_buffer_, hwaddr_str, hwaddr_length_);
-        memcpy(client_id_buffer_, client_id_str, client_id_length_);
+        convertFromBytea(r, row, HWADDR_COL, hwaddr_buffer_, 
+                         sizeof(hwaddr_buffer_), hwaddr_length_); 
 
-        PQfreemem(client_id_str);
-        PQfreemem(hwaddr_str);
+        convertFromBytea(r, row, CLIENT_ID_COL, client_id_buffer_, 
+                         sizeof(client_id_buffer_), client_id_length_); 
 
-        tmp.str(valid_lifetime_str);
-        tmp >> valid_lifetime;
-        valid_lifetime_ = static_cast<uint32_t>(valid_lifetime);
-        tmp.clear();
+        valid_lifetime_ = boost::lexical_cast<uint32_t>
+                          (PQgetvalue(r, row , VALID_LIFETIME_COL));
 
-        expire_ = convertFromDatabaseTime(expire_str);
+        expire_ = convertFromDatabaseTime(getColumnValue(r, row, EXPIRE_COL));
 
-        tmp.str(subnet_id_str);
-        tmp >> subnet_id;
-        subnet_id_ = static_cast<uint32_t>(subnet_id);
-        tmp.clear();
+        getColumnValue(r, row , SUBNET_ID_COL, subnet_id_); 
 
-        time_t cltt = expire_ - valid_lifetime_;
+        cltt_ = expire_ - valid_lifetime_;
 
-        // Extract fqdn_fwd, fqdn_rev
-        bool fwd = stringToBool(PQgetvalue(r, line, 6));
-        bool rev = stringToBool(PQgetvalue(r, line, 7));
+        getColumnValue(r, row, FQDN_FWD_COL, fqdn_fwd_);
+        getColumnValue(r, row, FQDN_REV_COL, fqdn_rev_);
 
-        // Extract hostname field
-        string hostname(PQgetvalue(r, line, 8));
+        hostname_ = getColumnValue(r, row, HOSTNAME_COL);
 
         return (Lease4Ptr(new Lease4(addr4_, hwaddr_buffer_, hwaddr_length_,
                                      client_id_buffer_, client_id_length_,
-                                     valid_lifetime_, 0, 0, cltt, subnet_id_,
-                                     fwd, rev, hostname)));
+                                     valid_lifetime_, 0, 0, cltt_, subnet_id_,
+                                     fqdn_fwd_, fqdn_rev_, hostname_)));
     }
 
 private:
@@ -376,13 +496,23 @@ private:
     uint8_t         client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
     Lease4Ptr       lease_;
     time_t          expire_;
+    time_t          cltt_;
     uint32_t        subnet_id_;
     uint32_t        valid_lifetime_;
     unsigned long   hwaddr_length_;
     unsigned long   client_id_length_;
+    bool fqdn_fwd_;
+    bool fqdn_rev_;
     string hostname_;
+
+    std::string addr4_str_;
+    std::string valid_lft_str_;
+    std::string expire_str_;
+    std::string subnet_id_str_;
+
 };
 
+
 class PgSqlLease6Exchange : public PgSqlLeaseExchange {
 private:
     static const size_t LEASE_COLUMNS = 12;
@@ -457,18 +587,18 @@ public:
     }
 
     Lease6Ptr
-    convertFromDatabase(PGresult * r, int line) {
-        const char* addr6_str = PQgetvalue(r, line, 0);
+    convertFromDatabase(PGresult * r, int row) {
+        const char* addr6_str = PQgetvalue(r, row, 0);
         unsigned char* duid_str =  PQunescapeBytea((const unsigned char *)
-                                                   PQgetvalue(r, line, 1),
+                                                   PQgetvalue(r, row, 1),
                                                    & duid_length_);
-        const char* valid_lifetime_str =  PQgetvalue(r, line, 2);
-        const char* expire_str = PQgetvalue(r, line, 3);
-        const char* subnet_id_str = PQgetvalue(r, line, 4);
-        const char* pref_lifetime_str = PQgetvalue(r, line, 5);
-        const char* lease_type_str = PQgetvalue(r, line, 6);
-        const char* iaid_str = PQgetvalue(r, line, 7);
-        const char* prefixlen_str = PQgetvalue(r, line, 8);
+        const char* valid_lifetime_str =  PQgetvalue(r, row, 2);
+        const char* expire_str = PQgetvalue(r, row, 3);
+        const char* subnet_id_str = PQgetvalue(r, row, 4);
+        const char* pref_lifetime_str = PQgetvalue(r, row, 5);
+        const char* lease_type_str = PQgetvalue(r, row, 6);
+        const char* iaid_str = PQgetvalue(r, row, 7);
+        const char* prefixlen_str = PQgetvalue(r, row, 8);
         unsigned int lease_type, prefixlen;
         unsigned long valid_lifetime, subnet_id, pref_lifetime, iaid;
 
@@ -533,11 +663,11 @@ public:
         }
 
         // Extract fqdn_fwd, fqdn_rev
-        bool fwd = stringToBool(PQgetvalue(r, line, 9));
-        bool rev = stringToBool(PQgetvalue(r, line, 10));
+        bool fwd = stringToBool(PQgetvalue(r, row, 9));
+        bool rev = stringToBool(PQgetvalue(r, row, 10));
 
         // Extract hostname field
-        string hostname(PQgetvalue(r, line, 11));
+        string hostname(PQgetvalue(r, row, 11));
 
         // Set up DUID,
         DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
@@ -671,6 +801,40 @@ PgSqlLeaseMgr::openDatabase() {
 
 bool
 PgSqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
+                              PsqlBindArray& bind_array) {
+
+    PGresult * r = PQexecPrepared(conn_, statements_[stindex].stmt_name,
+                                  statements_[stindex].stmt_nbparams,
+                                  &bind_array.values_[0], 
+                                  &bind_array.lengths_[0],
+                                  &bind_array.formats_[0], 0);
+
+    int s = PQresultStatus(r);
+    if (s != PGRES_COMMAND_OK) {
+        const char * errorMsg = PQerrorMessage(conn_);
+        PQclear(r);
+
+        /// @todo - ok, do we have to rely on error message text??
+        /// and why is failing on duplicate key NOT an error?
+        /// should be looking at global sqlca struct
+        if(!strncmp(errorMsg, "ERROR:  duplicate key",
+           sizeof("ERROR:  duplicate key") - 1)) {
+            return (false);
+        }
+
+        isc_throw(DbOperationError, "unable to INSERT for " <<
+                  statements_[stindex].stmt_name << ", reason: " <<
+                  errorMsg);
+    }
+
+    PQclear(r);
+
+    return (true);
+}
+
+
+bool
+PgSqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
                               BindParams& params) {
     vector<const char *> out_values;
     vector<int> out_lengths;
@@ -709,9 +873,10 @@ bool
 PgSqlLeaseMgr::addLease(const Lease4Ptr& lease) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_PGSQL_ADD_ADDR4).arg(lease->addr_.toText());
-    BindParams params = exchange4_->createBindForSend(lease);
 
-    return (addLeaseCommon(INSERT_LEASE4, params));
+    PsqlBindArray bind_array;
+    exchange4_->createBindForSend(lease, bind_array);
+    return (addLeaseCommon(INSERT_LEASE4, bind_array));
 }
 
 bool
@@ -743,15 +908,15 @@ void PgSqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
 
     checkStatementError(r, stindex);
 
-    int lines = PQntuples(r);
-    if (single && lines > 1) {
+    int rows = PQntuples(r);
+    if (single && rows > 1) {
         PQclear(r);
         isc_throw(MultipleRecords, "multiple records were found in the "
                       "database where only one was expected for query "
                       << statements_[stindex].stmt_name);
     }
 
-    for(int i = 0; i < lines; ++ i) {
+    for(int i = 0; i < rows; ++ i) {
         result.push_back(exchange->convertFromDatabase(r, i));
     }
 
@@ -1009,6 +1174,43 @@ PgSqlLeaseMgr::getLeases6(Lease::Type lease_type, const DUID& duid,
 
 template <typename LeasePtr>
 void
+PgSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, 
+                                 PsqlBindArray& bind_array, 
+                                 const LeasePtr& lease) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_PGSQL_ADD_ADDR4).arg(statements_[stindex].stmt_name);
+
+    PGresult * r = PQexecPrepared(conn_, statements_[stindex].stmt_name,
+                                  statements_[stindex].stmt_nbparams,
+                                  &bind_array.values_[0], 
+                                  &bind_array.lengths_[0], 
+                                  &bind_array.formats_[0], 0);
+
+    checkStatementError(r, stindex);
+
+    int affected_rows = boost::lexical_cast<int>(PQcmdTuples(r));
+    PQclear(r);
+
+    // Check success case first as it is the most likely outcome.
+    if (affected_rows == 1) {
+      return;
+    }
+
+    // If no rows affected, lease doesn't exist.
+    if (affected_rows == 0) {
+        isc_throw(NoSuchLease, "unable to update lease for address " <<
+                  lease->addr_.toText() << " as it does not exist");
+    }
+
+    // Should not happen - primary key constraint should only have selected
+    // one row.
+    isc_throw(DbOperationError, "apparently updated more than one lease "
+                  "that had the address " << lease->addr_.toText());
+}
+
+
+template <typename LeasePtr>
+void
 PgSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, BindParams & params,
                                  const LeasePtr& lease) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
@@ -1052,15 +1254,16 @@ PgSqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
               DHCPSRV_PGSQL_UPDATE_ADDR4).arg(lease->addr_.toText());
 
     // Create the BIND array for the data being updated
-    ostringstream tmp;
-    BindParams params = exchange4_->createBindForSend(lease);
+    PsqlBindArray bind_array;
+    exchange4_->createBindForSend(lease, bind_array);
 
     // Set up the WHERE clause and append it to the SQL_BIND array
-    tmp << static_cast<uint32_t>(lease->addr_);
-    params.push_back(PgSqlParam(tmp.str()));
+    std::string addr4_ = boost::lexical_cast<std::string>
+                         (static_cast<uint32_t>(lease->addr_));
+    bind_array.add(addr4_);
 
     // Drop to common update code
-    updateLeaseCommon(stindex, params, lease);
+    updateLeaseCommon(stindex, bind_array, lease);
 }
 
 void
@@ -1139,7 +1342,7 @@ PgSqlLeaseMgr::checkStatementError(PGresult* r, StatementIndex index) const {
     }
 }
 
-inline void
+void
 PgSqlLeaseMgr::convertToQuery(const BindParams& params,
                               std::vector<const char *>& out_values,
                               std::vector<int>& out_lengths,

+ 31 - 0
src/lib/dhcpsrv/pgsql_lease_mgr.h

@@ -57,6 +57,31 @@ struct PgSqlParam {
     }
 };
 
+struct PsqlBindArray {
+    std::vector<const char *> values_;
+    std::vector<int> lengths_;
+    std::vector<int> formats_;
+
+    static const int TEXT_FMT;
+    static const int BINARY_FMT;
+    static const char* TRUE_STR;
+    static const char* FALSE_STR;
+
+    size_t size() {
+        return (values_.size());
+    }
+
+    bool empty() {
+        return (values_.empty());
+    }
+
+    void add(const char* value);
+    void add(const std::string& value);
+    void add(const std::vector<uint8_t>& data);
+    void add(const uint32_t& value);
+    void add(const bool& value);
+};
+
 /// @brief Defines all parameters for binding a compiled statement
 typedef std::vector<PgSqlParam> BindParams;
 
@@ -434,6 +459,8 @@ private:
     ///        failed.
     bool addLeaseCommon(StatementIndex stindex, BindParams& params);
 
+    bool addLeaseCommon(StatementIndex stindex, PsqlBindArray& bind_array);
+
     /// @brief Get Lease Collection Common Code
     ///
     /// This method performs the common actions for obtaining multiple leases
@@ -569,6 +596,10 @@ private:
     void updateLeaseCommon(StatementIndex stindex, BindParams& params,
                            const LeasePtr& lease);
 
+    template <typename LeasePtr>
+    void updateLeaseCommon(StatementIndex stindex, PsqlBindArray& bind_array,
+                           const LeasePtr& lease);
+
     /// @brief Delete lease common code
     ///
     /// Holds the common code for deleting a lease.  It binds the parameters