comments in-line below On 2015-02-11 15:59, David Mandelberg wrote: > From: David Mandelberg <dmand...@bbn.com> > > 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 | 446 > +++++++++++++++++++++++++++------------------------ > lib/db/prep-stmt.c | 18 ++- > 2 files changed, 246 insertions(+), 218 deletions(-) > > diff --git a/lib/db/clients/rtr.c b/lib/db/clients/rtr.c > index c19702e..0527482 100644 > --- a/lib/db/clients/rtr.c > +++ b/lib/db/clients/rtr.c > @@ -23,26 +23,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.
nit: This line and the rest of this structure has tab/spaces mismatch vs. rest of file. > + */ > + uint32_t ser_num; > + > + /** > + @brief The last row that we sent. > + */ > + struct last_row { nit: no need to name this structure -- you can do struct {...} > + /** > + @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. > + */ > + uint8_t first_time; should have type unsigned char (type of MYSQL_TYPE_TINY) > + > + uint32_t asn; should have type unsigned int (type of MYSQL_TYPE_LONG) > + > + /** > + @brief Length of #prefix, either 4 for IPv4 or 16 for IPv6. > + */ > + uint8_t prefix_family_length; > + > + uint8_t prefix[16]; should have type unsigned char[] (type of MYSQL_TYPE_BLOB) > + > + uint8_t prefix_length; should have type unsigned char (type of MYSQL_TYPE_TINY) > + > + uint8_t prefix_max_length; should have type unsigned char (type of MYSQL_TYPE_TINY) > + } first_row; Why is this called first_row when the name of the struct is last_row? Don't you mean: struct last_row {...} last_row or: struct first_row {...} 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( > @@ -476,164 +515,42 @@ 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 nit: please wrap at ~70 cols > + * 4 (IPv4) or 16 (IPv6). > + * @param[in] prefix_length Length in bits of the prefix. This must be >= 0, > and nit: please wrap at ~70 cols nit: it's unsigned, so how could it be < 0? > + * <= 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; > 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: > + fill_pdu_ipv4_prefix(pdu, flags, prefix_length, > + prefix_max_length, (struct in_addr const *)prefix_data, asn); s_addr isn't guaranteed to be the first member of struct in_addr, and the alignment of prefix_data might not be appropriate for struct in_addr. Should instead create a local struct in_addr and memcpy into it's s_addr member. > + 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: > + fill_pdu_ipv6_prefix(pdu, flags, prefix_length, > + prefix_max_length, (struct in6_addr const *)prefix_data, > asn); s6_addr isn't guaranteed to be the first member of struct in6_addr, and the alignment of prefix_data might not be appropriate for struct in6_addr. Should instead create a local struct in6_addr and memcpy into it's s6_addr member. > + break; > + > + default: > + return -1; > + } Would be nice to use an address family type instead of assuming that each address family has a different length. Not sure if/when we'll see another address family with the same length... > > return 0; > } > @@ -658,9 +575,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; It's OK/necesssary to not initialize state->session? nit: use a compound literal to replace all those lines: *state = (struct query_state){ .first_row.first_time = 1 }; > *query_state = (void *)state; > @@ -806,24 +723,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; state->first_row.first_time has type uint8_t, not unsigned char > + bind_in[1].is_unsigned = (my_bool)1; nit: no need for casting (also below) > + bind_in[1].is_null = (my_bool *)0; nit: NULL (also below) > + // 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; state->first_row.asn has type uint32_t, not unsigned int > + 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; what about .is_unsigned? > + // prefix_length parameter > + bind_in[4].buffer_type = MYSQL_TYPE_TINY; > + bind_in[4].buffer = &state->first_row.prefix_length; state->first_row.prefix_length has type uint8_t, not unsigned char > + 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; state->first_row.prefix_max_length has type uint8_t, not unsigned char > + 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; > + uint64_t limit = max_rows - *num_pdus; type should be unsigned long long, not uint64_t > + bind_in[6].buffer = &limit; > + bind_in[6].is_unsigned = (my_bool) 1; > + bind_in[6].is_null = (my_bool *) 0; nit: use an initializer > + > > if (mysql_stmt_bind_param(stmt, bind_in)) > { > @@ -839,23 +787,35 @@ static int serial_query_do_query( > return -1; > } > > - MYSQL_BIND bind_out[3]; > + MYSQL_BIND bind_out[5]; > uint32_t db_asn; > - char db_ip_addr[IPADDR_STR_LEN + 1]; > + unsigned long db_prefix_family_length; > + uint8_t db_prefix[16]; should have type unsigned char[] (type of MYSQL_TYPE_BLOB) > + uint8_t db_prefix_length; should have type unsigned char (type of MYSQL_TYPE_TINY) > + uint8_t db_prefix_max_length; should have type unsigned char (type of MYSQL_TYPE_TINY) > int8_t 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; what about .is_unsigned? I guess it's memset to 0. Do you want that? (The type of db_prefix is unsigned array.) > + // 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; nit: use an initializer > > if (mysql_stmt_bind_result(stmt, bind_out)) > { > @@ -880,15 +840,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; It took me a little while to understand why this information is now stored in state when it wasn't previously, so a comment (or at least an explanation in the commit message) about how this all works would be useful. > } > if (ret != 0 && ret != MYSQL_NO_DATA) > { > @@ -946,7 +916,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) > @@ -1063,7 +1033,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; > @@ -1162,24 +1132,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; > + uint64_t 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; ~50 lines of almost purely identical code makes me sad. > > if (mysql_stmt_bind_param(stmt, bind_in)) > { > @@ -1199,18 +1199,30 @@ ssize_t db_rtr_reset_query_get_next( > return -1; > } > > - MYSQL_BIND bind_out[2]; > + MYSQL_BIND bind_out[4]; > uint32_t db_asn; > - char db_ip_addr[IPADDR_STR_LEN + 1]; > + unsigned long db_prefix_family_length; > + uint8_t db_prefix[16]; > + uint8_t db_prefix_length; > + uint8_t 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; and this is also almost identical to an above hunk of code (with similar issues) > > if (mysql_stmt_bind_result(stmt, bind_out)) > { > @@ -1240,8 +1252,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)) more nearly identical code > { > LOG(LOG_ERR, "could not create PDU_IPVx_PREFIX"); > mysql_stmt_free_result(stmt); > @@ -1250,7 +1266,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; more nearly identical code -Richard > } > 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", > ------------------------------------------------------------------------------ _______________________________________________ rpstir-devel mailing list rpstir-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/rpstir-devel