Use the schema introduced in the previous set of commits when querying
rpki-rtr data from the database. Additionally, change how rpki-rtr
data is queried during Serial and Reset Queries to significantly
improve performance with large rtr_incremental or rtr_full tables.

NOTE: This is one of a series of commits to use the updated schema.
It is the last in the series to update the non-test code, so the code
should be working now, but the tests will still fail.

addresses [#5] and [#7]
---
 lib/db/clients/rtr.c | 456 +++++++++++++++++++++++++++------------------------
 lib/db/prep-stmt.c   |  18 +-
 2 files changed, 253 insertions(+), 221 deletions(-)

diff --git a/lib/db/clients/rtr.c b/lib/db/clients/rtr.c
index 6c0a553..e53bd0a 100644
--- a/lib/db/clients/rtr.c
+++ b/lib/db/clients/rtr.c
@@ -25,26 +25,65 @@
 
 
 struct query_state {
-    uint32_t ser_num;           // ser_num to search for first row to send
-    uint64_t first_row;         // first row to send.  zero-based
-    int bad_ser_num;            // neither the given ser_num, nor its
-                                // successor, exist
-    int data_sent;              // true if a pdu has been created for this
-                                // serial/reset query
-    int no_new_data;            // the given ser_num exists, but its successor 
-                                // does not
-    int not_ready;              // no valid ser_nums exist, yet
-    uint16_t session;           // the session_id number
+    /**
+        @brief Serial number to search for first row to send.
+    */
+    uint32_t ser_num;
+
+    /**
+        @brief The last row that we sent.
+    */
+    struct last_row {
+        /**
+            @brief False if there was a previous row, true if this is
+                the first time.
+
+            If this is true, the values in the remaining fields are
+            unspecified.
+        */
+        unsigned char first_time;
+
+        unsigned asn;
+
+        /**
+            @brief Length of #prefix, either 4 for IPv4 or 16 for IPv6.
+        */
+        uint8_t prefix_family_length;
+
+        unsigned char prefix[16];
+
+        unsigned char prefix_length;
+
+        unsigned char prefix_max_length;
+    } first_row;
+
+    /**
+        @brief Neither the given ser_num, nor its successor, exist.
+    */
+    int bad_ser_num;
+
+    /**
+        @brief True if a pdu has been created for this serial/reset query.
+    */
+    int data_sent;
+
+    /**
+        @brief The given ser_num exists, but its successor does not.
+    */
+    int no_new_data;
+
+    /**
+        @brief No valid ser_nums exist, yet.
+    */
+    int not_ready;
+
+    /**
+        @brief The session_id number.
+    */
+    uint16_t session;
 };
 
 
-static const size_t IPADDR_STR_LEN = ((INET6_ADDRSTRLEN > INET_ADDRSTRLEN) ? 
INET6_ADDRSTRLEN : INET_ADDRSTRLEN) + 1 +  // '/'
-    3 +                         // prefix length
-    1 +                         // '('
-    3 +                         // max length
-    1;                          // ')'
-
-
 
/**=============================================================================
 
------------------------------------------------------------------------------*/
 int db_rtr_get_session_id(
@@ -478,164 +517,46 @@ static int readSerNumAsCurrent(
 
 
 
/**=============================================================================
- * @param field_str has the format:  <address>/<length>[(<max_length>)]
- * It originates from a database field `ip_addr' and gets null terminated
- *     before being passed to this function.
- * @return 0 on success or an error code on failure.
-------------------------------------------------------------------------------*/
-static int parseIpaddr(
-    sa_family_t * family,
-    struct in_addr *addr4,
-    struct in6_addr *addr6,
-    uint8_t * prefix_len,
-    uint8_t * max_len,
-    const char field_str[])
-{
-    char ip_txt[INET_ADDRSTRLEN >
-                INET6_ADDRSTRLEN ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN];
-    size_t i;
-    int chars_consumed;
-
-    if (field_str[0] == '\0')
-    {
-        LOG(LOG_ERR, "empty field string");
-        return -1;
-    }
-
-    // copy IP field
-    for (i = 0;
-         field_str[i] != '\0' && field_str[i] != '/' && i < sizeof(ip_txt);
-         ++i)
-    {
-        ip_txt[i] = field_str[i];
-    }
-    if (field_str[i] == '\0')
-    {
-        LOG(LOG_ERR, "no prefix length present");
-        return -1;
-    }
-    else if (field_str[i] == '/')
-    {
-        ip_txt[i] = '\0';
-        ++i;
-    }
-    else
-    {
-        LOG(LOG_ERR, "IP address string too long");
-        return -1;
-    }
-
-    // parse IP field
-    if (inet_pton(AF_INET, ip_txt, addr4) == 1)
-    {
-        *family = AF_INET;
-    }
-    else if (inet_pton(AF_INET6, ip_txt, addr6) == 1)
-    {
-        *family = AF_INET6;
-    }
-    else
-    {
-        LOG(LOG_ERR, "malformed IP address");
-        return -1;
-    }
-
-    // parse prefix length field
-    if (sscanf(field_str + i, "%" SCNu8 "%n", prefix_len, &chars_consumed) < 1)
-    {
-        LOG(LOG_ERR, "error parsing prefix length");
-        return -1;
-    }
-    else
-    {
-        i += chars_consumed;
-    }
-
-    // return early if there's no max length field
-    if (field_str[i] == '\0')
-    {
-        *max_len = *prefix_len;
-        return 0;
-    }
-
-    // parse max length field
-    if (field_str[i] == '(')
-    {
-        ++i;
-    }
-    else
-    {
-        LOG(LOG_ERR, "expecting `(' after the prefix length");
-        return -1;
-    }
-
-    if (sscanf(field_str + i, "%" SCNu8 "%n", max_len, &chars_consumed) < 1)
-    {
-        LOG(LOG_ERR, "error parsing max length");
-        return -1;
-    }
-    else
-    {
-        i += chars_consumed;
-    }
-
-    if (field_str[i] == '\0')
-    {
-        LOG(LOG_ERR, "truncated max length");
-        return -1;
-    }
-    else if (field_str[i] != ')')
-    {
-        LOG(LOG_ERR, "garbage at end of max length field");
-        return -1;
-    }
-    else
-    {
-        ++i;
-    }
-
-    // done all parsing
-
-    if (field_str[i] != '\0')
-    {
-        LOG(LOG_ERR, "garbage at end");
-        return -1;
-    }
-
-    return 0;
-}
-
-
-/**=============================================================================
+ * @param[out] pdu PDU to fill in.
+ * @param[in] asn AS number.
+ * @param[in] prefix_data Raw prefix.
+ * @param[in] prefix_data_length Length of @p prefix_data. This must be either
+ *    4 (IPv4) or 16 (IPv6).
+ * @param[in] prefix_length Length in bits of the prefix. This must be >= 0, 
and
+ *    <= 32 for IPv4 or <= 128 for IPv6.
+ * @param[in] prefix_max_length Maximum prefix length, as from a ROA.
+ * @param[in] is_announce True for announcement, false for withdrawal.
 
------------------------------------------------------------------------------*/
 static int fillPduIpPrefix(
     PDU * pdu,
     uint32_t asn,
-    char *ip_addr,
+    uint8_t const * prefix_data,
+    size_t prefix_data_length,
+    uint8_t prefix_length,
+    uint8_t prefix_max_length,
     bool is_announce)
 {
-    sa_family_t family = 0;
-    struct in_addr addr4;
-    struct in6_addr addr6;
-    uint8_t prefix_len;
-    uint8_t max_prefix_len;
+    struct in_addr in_addr;
+    struct in6_addr in6_addr;
     uint8_t flags = is_announce ? FLAG_WITHDRAW_ANNOUNCE : 0;
 
-    if (parseIpaddr(&family, &addr4, &addr6, &prefix_len, &max_prefix_len,
-                    ip_addr))
+    switch (prefix_data_length)
     {
-        LOG(LOG_ERR, "could not parse ip_addr");
-        return -1;
-    }
+        case 4:
+            memcpy(&in_addr.s_addr, prefix_data, prefix_data_length);
+            fill_pdu_ipv4_prefix(pdu, flags, prefix_length,
+                prefix_max_length, &in_addr, asn);
+            break;
 
-    if (family == AF_INET)
-        fill_pdu_ipv4_prefix(pdu,
-                             flags, prefix_len, max_prefix_len, &addr4, asn);
-    else if (family == AF_INET6)
-        fill_pdu_ipv6_prefix(pdu,
-                             flags, prefix_len, max_prefix_len, &addr6, asn);
-    else
-        return -1;
+        case 16:
+            memcpy(&in6_addr.s6_addr, prefix_data, prefix_data_length);
+            fill_pdu_ipv6_prefix(pdu, flags, prefix_length,
+                prefix_max_length, &in6_addr, asn);
+            break;
+
+        default:
+            return -1;
+    }
 
     return 0;
 }
@@ -660,9 +581,9 @@ int db_rtr_serial_query_init(
         return -1;
     }
     state->ser_num = 0;
-    state->first_row = 0;
-    state->data_sent = 0;
+    state->first_row.first_time = 1;
     state->bad_ser_num = 0;
+    state->data_sent = 0;
     state->no_new_data = 0;
     state->not_ready = 0;
     *query_state = (void *)state;
@@ -809,24 +730,55 @@ static int serial_query_do_query(
     MYSQL_STMT *stmt =
         conn->stmts[DB_CLIENT_TYPE_RTR][DB_PSTMT_RTR_SERIAL_QRY_GET_NEXT];
 
-    MYSQL_BIND bind_in[3];
+    MYSQL_BIND bind_in[7];
     memset(bind_in, 0, sizeof(bind_in));
     // serial_num parameter
     bind_in[0].buffer_type = MYSQL_TYPE_LONG;
     bind_in[0].buffer = &(state->ser_num);
     bind_in[0].is_unsigned = (my_bool) 1;
     bind_in[0].is_null = (my_bool *) 0;
-    // offset parameter
-    bind_in[1].buffer_type = MYSQL_TYPE_LONGLONG;
-    bind_in[1].buffer = &(state->first_row);
-    bind_in[1].is_unsigned = (my_bool) 1;
-    bind_in[1].is_null = (my_bool *) 0;
-    // limit parameter
+    // start-at-beginning parameter
+    bind_in[1].buffer_type = MYSQL_TYPE_TINY;
+    bind_in[1].buffer = &state->first_row.first_time;
+    bind_in[1].is_unsigned = (my_bool)1;
+    bind_in[1].is_null = (my_bool *)0;
+    // asn parameter
     bind_in[2].buffer_type = MYSQL_TYPE_LONG;
-    size_t limit = max_rows - *num_pdus;
-    bind_in[2].buffer = &limit;
-    bind_in[2].is_unsigned = (my_bool) 1;
-    bind_in[2].is_null = (my_bool *) 0;
+    bind_in[2].buffer = &state->first_row.asn;
+    bind_in[2].is_unsigned = (my_bool)1;
+    bind_in[2].is_null = (my_bool *)0;
+    // prefix parameter
+    bind_in[3].buffer_type = MYSQL_TYPE_BLOB;
+    bind_in[3].buffer = &state->first_row.prefix;
+    bind_in[3].buffer_length = sizeof(state->first_row.prefix);
+    unsigned long prefix_family_length;
+    if (state->first_row.first_time)
+    {
+        prefix_family_length = 0;
+    }
+    else
+    {
+        prefix_family_length = state->first_row.prefix_family_length;
+    }
+    bind_in[3].length = &prefix_family_length;
+    bind_in[3].is_null = (my_bool *)0;
+    // prefix_length parameter
+    bind_in[4].buffer_type = MYSQL_TYPE_TINY;
+    bind_in[4].buffer = &state->first_row.prefix_length;
+    bind_in[4].is_unsigned = (my_bool)1;
+    bind_in[4].is_null = (my_bool *)0;
+    // prefix_max_length parameter
+    bind_in[5].buffer_type = MYSQL_TYPE_TINY;
+    bind_in[5].buffer = &state->first_row.prefix_max_length;
+    bind_in[5].is_unsigned = (my_bool)1;
+    bind_in[5].is_null = (my_bool *)0;
+    // limit parameter
+    bind_in[6].buffer_type = MYSQL_TYPE_LONGLONG;
+    unsigned long long limit = max_rows - *num_pdus;
+    bind_in[6].buffer = &limit;
+    bind_in[6].is_unsigned = (my_bool) 1;
+    bind_in[6].is_null = (my_bool *) 0;
+
 
     if (mysql_stmt_bind_param(stmt, bind_in))
     {
@@ -842,23 +794,35 @@ static int serial_query_do_query(
         return -1;
     }
 
-    MYSQL_BIND bind_out[3];
-    uint32_t db_asn;
-    char db_ip_addr[IPADDR_STR_LEN + 1];
-    int8_t db_is_announce;
+    MYSQL_BIND bind_out[5];
+    unsigned db_asn;
+    unsigned long db_prefix_family_length;
+    unsigned char db_prefix[16];
+    unsigned char db_prefix_length;
+    unsigned char db_prefix_max_length;
+    signed char db_is_announce;
     memset(bind_out, 0, sizeof(bind_out));
     // asn output
     bind_out[0].buffer_type = MYSQL_TYPE_LONG;
     bind_out[0].is_unsigned = (my_bool) 1;
     bind_out[0].buffer = &db_asn;
-    // ip_addr output
-    bind_out[1].buffer_type = MYSQL_TYPE_STRING;
-    bind_out[1].buffer_length = IPADDR_STR_LEN;
-    bind_out[1].buffer = (char *)&db_ip_addr;
-    // is_announce output
+    // prefix output
+    bind_out[1].buffer_type = MYSQL_TYPE_BLOB;
+    bind_out[1].buffer_length = sizeof(db_prefix);
+    bind_out[1].length = &db_prefix_family_length;
+    bind_out[1].buffer = &db_prefix;
+    // prefix_length output
     bind_out[2].buffer_type = MYSQL_TYPE_TINY;
-    bind_out[2].is_unsigned = (my_bool) 0;
-    bind_out[2].buffer = &db_is_announce;
+    bind_out[2].is_unsigned = (my_bool)1;
+    bind_out[2].buffer = &db_prefix_length;
+    // prefix_max_length output
+    bind_out[3].buffer_type = MYSQL_TYPE_TINY;
+    bind_out[3].is_unsigned = (my_bool)1;
+    bind_out[3].buffer = &db_prefix_max_length;
+    // is_announce output
+    bind_out[4].buffer_type = MYSQL_TYPE_TINY;
+    bind_out[4].is_unsigned = (my_bool) 0;
+    bind_out[4].buffer = &db_is_announce;
 
     if (mysql_stmt_bind_result(stmt, bind_out))
     {
@@ -883,15 +847,25 @@ static int serial_query_do_query(
 
     while ((ret = mysql_stmt_fetch(stmt)) == 0)
     {
-        if (fillPduIpPrefix(&((*_pdus)[*num_pdus]), db_asn, db_ip_addr, 
db_is_announce  /* , 
-                                                                               
          * state->session */ ))
+        if (fillPduIpPrefix(
+            &((*_pdus)[*num_pdus]),
+            db_asn,
+            db_prefix, db_prefix_family_length,
+            db_prefix_length, db_prefix_max_length,
+            db_is_announce))
         {
             LOG(LOG_ERR, "could not create PDU_IPVx_PREFIX");
             mysql_stmt_free_result(stmt);
             return -1;
         }
         ++*num_pdus;
-        ++state->first_row;
+
+        state->first_row.first_time = 0;
+        state->first_row.asn = db_asn;
+        state->first_row.prefix_family_length = db_prefix_family_length;
+        memcpy(state->first_row.prefix, db_prefix, db_prefix_family_length);
+        state->first_row.prefix_length = db_prefix_length;
+        state->first_row.prefix_max_length = db_prefix_max_length;
     }
     if (ret != 0 && ret != MYSQL_NO_DATA)
     {
@@ -949,7 +923,7 @@ static int serial_query_post_query(
     {                           // db has sn for this sn_prev
         *is_done = 0;
         state->ser_num = next_ser_num;
-        state->first_row = 0;
+        state->first_row.first_time = 1;
         return 0;
     }
     else if (ret == GET_SERNUM_NONE)
@@ -1066,7 +1040,7 @@ int db_rtr_reset_query_init(
         return -1;
     }
     state->ser_num = 0;
-    state->first_row = 0;
+    state->first_row.first_time = 1;
     state->bad_ser_num = 0;
     state->data_sent = 0;
     state->no_new_data = 0;
@@ -1165,24 +1139,54 @@ ssize_t db_rtr_reset_query_get_next(
 
     MYSQL_STMT *stmt =
         conn->stmts[DB_CLIENT_TYPE_RTR][DB_PSTMT_RTR_RESET_QRY_GET_NEXT];
-    MYSQL_BIND bind_in[3];
+    MYSQL_BIND bind_in[7];
     memset(bind_in, 0, sizeof(bind_in));
     // serial_num parameter
     bind_in[0].buffer_type = MYSQL_TYPE_LONG;
     bind_in[0].buffer = &(state->ser_num);
     bind_in[0].is_unsigned = (my_bool) 1;
     bind_in[0].is_null = (my_bool *) 0;
-    // offset parameter
-    bind_in[1].buffer_type = MYSQL_TYPE_LONGLONG;
-    bind_in[1].buffer = &(state->first_row);
-    bind_in[1].is_unsigned = (my_bool) 1;
-    bind_in[1].is_null = (my_bool *) 0;
-    // limit parameter
+    // start-at-beginning parameter
+    bind_in[1].buffer_type = MYSQL_TYPE_TINY;
+    bind_in[1].buffer = &state->first_row.first_time;
+    bind_in[1].is_unsigned = (my_bool)1;
+    bind_in[1].is_null = (my_bool *)0;
+    // asn parameter
     bind_in[2].buffer_type = MYSQL_TYPE_LONG;
-    size_t limit = max_rows - num_pdus;
-    bind_in[2].buffer = &limit;
-    bind_in[2].is_unsigned = (my_bool) 1;
-    bind_in[2].is_null = (my_bool *) 0;
+    bind_in[2].buffer = &state->first_row.asn;
+    bind_in[2].is_unsigned = (my_bool)1;
+    bind_in[2].is_null = (my_bool *)0;
+    // prefix parameter
+    bind_in[3].buffer_type = MYSQL_TYPE_BLOB;
+    bind_in[3].buffer = &state->first_row.prefix;
+    bind_in[3].buffer_length = sizeof(state->first_row.prefix);
+    unsigned long prefix_family_length;
+    if (state->first_row.first_time)
+    {
+        prefix_family_length = 0;
+    }
+    else
+    {
+        prefix_family_length = state->first_row.prefix_family_length;
+    }
+    bind_in[3].length = &prefix_family_length;
+    bind_in[3].is_null = (my_bool *)0;
+    // prefix_length parameter
+    bind_in[4].buffer_type = MYSQL_TYPE_TINY;
+    bind_in[4].buffer = &state->first_row.prefix_length;
+    bind_in[4].is_unsigned = (my_bool)1;
+    bind_in[4].is_null = (my_bool *)0;
+    // prefix_max_length parameter
+    bind_in[5].buffer_type = MYSQL_TYPE_TINY;
+    bind_in[5].buffer = &state->first_row.prefix_max_length;
+    bind_in[5].is_unsigned = (my_bool)1;
+    bind_in[5].is_null = (my_bool *)0;
+    // limit parameter
+    bind_in[6].buffer_type = MYSQL_TYPE_LONGLONG;
+    unsigned long long limit = max_rows - num_pdus;
+    bind_in[6].buffer = &limit;
+    bind_in[6].is_unsigned = (my_bool) 1;
+    bind_in[6].is_null = (my_bool *) 0;
 
     if (mysql_stmt_bind_param(stmt, bind_in))
     {
@@ -1202,18 +1206,30 @@ ssize_t db_rtr_reset_query_get_next(
         return -1;
     }
 
-    MYSQL_BIND bind_out[2];
-    uint32_t db_asn;
-    char db_ip_addr[IPADDR_STR_LEN + 1];
+    MYSQL_BIND bind_out[4];
+    unsigned db_asn;
+    unsigned long db_prefix_family_length;
+    unsigned char db_prefix[16];
+    unsigned char db_prefix_length;
+    unsigned char db_prefix_max_length;
     memset(bind_out, 0, sizeof(bind_out));
     // asn output
     bind_out[0].buffer_type = MYSQL_TYPE_LONG;
     bind_out[0].is_unsigned = 1;
     bind_out[0].buffer = &db_asn;
-    // ip_addr output
-    bind_out[1].buffer_type = MYSQL_TYPE_STRING;
-    bind_out[1].buffer_length = IPADDR_STR_LEN;
-    bind_out[1].buffer = (char *)&db_ip_addr;
+    // prefix output
+    bind_out[1].buffer_type = MYSQL_TYPE_BLOB;
+    bind_out[1].buffer_length = sizeof(db_prefix);
+    bind_out[1].length = &db_prefix_family_length;
+    bind_out[1].buffer = &db_prefix;
+    // prefix_length output
+    bind_out[2].buffer_type = MYSQL_TYPE_TINY;
+    bind_out[2].is_unsigned = (my_bool)1;
+    bind_out[2].buffer = &db_prefix_length;
+    // prefix_max_length output
+    bind_out[3].buffer_type = MYSQL_TYPE_TINY;
+    bind_out[3].is_unsigned = (my_bool)1;
+    bind_out[3].buffer = &db_prefix_max_length;
 
     if (mysql_stmt_bind_result(stmt, bind_out))
     {
@@ -1243,8 +1259,12 @@ ssize_t db_rtr_reset_query_get_next(
     int ret;
     while ((ret = mysql_stmt_fetch(stmt)) == 0)
     {
-        if (fillPduIpPrefix(&((*_pdus)[num_pdus]), db_asn, db_ip_addr, 1       
 /* , 
-                                                                               
  * state->session */ ))
+        if (fillPduIpPrefix(
+            &((*_pdus)[num_pdus]),
+            db_asn,
+            db_prefix, db_prefix_family_length,
+            db_prefix_length, db_prefix_max_length,
+            1))
         {
             LOG(LOG_ERR, "could not create PDU_IPVx_PREFIX");
             mysql_stmt_free_result(stmt);
@@ -1253,7 +1273,13 @@ ssize_t db_rtr_reset_query_get_next(
             return -1;
         }
         ++num_pdus;
-        ++state->first_row;
+
+        state->first_row.first_time = 0;
+        state->first_row.asn = db_asn;
+        state->first_row.prefix_family_length = db_prefix_family_length;
+        memcpy(state->first_row.prefix, db_prefix, db_prefix_family_length);
+        state->first_row.prefix_length = db_prefix_length;
+        state->first_row.prefix_max_length = db_prefix_max_length;
     }
     if (ret != 0 && ret != MYSQL_NO_DATA)
     {
diff --git a/lib/db/prep-stmt.c b/lib/db/prep-stmt.c
index 214a9a6..a7a778d 100644
--- a/lib/db/prep-stmt.c
+++ b/lib/db/prep-stmt.c
@@ -27,14 +27,20 @@ static const char *_queries_rtr[] = {
         " from rtr_update " " where serial_num=?",
 
     // DB_PSTMT_RTR_SERIAL_QRY_GET_NEXT
-    "select asn, ip_addr, is_announce "
-        " from rtr_incremental "
-        " where serial_num=? " " order by asn, ip_addr " " limit ?, ?",
+    "select asn, prefix, prefix_length, prefix_max_length, is_announce "
+    " from rtr_incremental "
+    " where serial_num=? "
+    "  and (? or (asn, prefix, prefix_length, prefix_max_length) > (?, ?, ?, 
?)) "
+    " order by asn, prefix, prefix_length, prefix_max_length "
+    " limit ?",
 
     // DB_PSTMT_RTR_RESET_QRY_GET_NEXT
-    "select asn, ip_addr "
-        " from rtr_full "
-        " where serial_num=? " " order by asn, ip_addr " " limit ?, ?",
+    "select asn, prefix, prefix_length, prefix_max_length "
+    " from rtr_full "
+    " where serial_num=? "
+    "  and (? or (asn, prefix, prefix_length, prefix_max_length) > (?, ?, ?, 
?)) "
+    " order by asn, prefix, prefix_length, prefix_max_length "
+    " limit ?",
 
     // DB_PSTMT_RTR_COUNT_SESSION
     "select count(*) from rtr_session",
-- 
1.9.1


------------------------------------------------------------------------------
_______________________________________________
rpstir-devel mailing list
rpstir-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/rpstir-devel

Reply via email to