From 4c75fe3a275af77a2ec77af5b43b31174bbdfbb8 Mon Sep 17 00:00:00 2001 From: kanoi Date: Thu, 11 Dec 2014 15:52:44 +1100 Subject: [PATCH] ckdb/php - add code for future support of multiple payout addresses per user --- pool/db.php | 12 +- pool/page_settings.php | 8 +- src/ckdb.h | 15 ++- src/ckdb_cmd.c | 144 +++++++++++++++++----- src/ckdb_data.c | 35 ++++-- src/ckdb_dbio.c | 272 +++++++++++++++++++++++++++-------------- 6 files changed, 350 insertions(+), 136 deletions(-) diff --git a/pool/db.php b/pool/db.php index f8764876..02028a90 100644 --- a/pool/db.php +++ b/pool/db.php @@ -180,7 +180,17 @@ function userSettings($user, $email = null, $addr = null, $pass = null) $flds['email'] = $email; if ($addr != null) { - $flds['address'] = $addr; + $rows = count($addr); + $i = 0; + foreach ($addr as $ar) + { + $flds['address:'.$i] = $ar['addr']; + // optional - missing = use default + if (isset($ar['ratio'])) + $flds['ratio:'.$i] = $ar['ratio']; + $i++; + } + $flds['rows'] = $rows; $tmo = 3; # 3x the timeout } if ($pass != null) diff --git a/pool/page_settings.php b/pool/page_settings.php index 852e3f1a..5838dfdc 100644 --- a/pool/page_settings.php +++ b/pool/page_settings.php @@ -102,8 +102,9 @@ function dosettings($data, $user) break; case 'Address': $addr = getparam('baddr', false); + $addrarr = array(array('addr' => $addr)); $pass = getparam('pass', false); - $ans = userSettings($user, null, $addr, $pass); + $ans = userSettings($user, null, $addrarr, $pass); $check = true; break; case 'Password': @@ -139,8 +140,9 @@ function dosettings($data, $user) $email = $ans['email']; else $email = ''; - if (isset($ans['addr'])) - $addr = $ans['addr']; + // Use the first one - updating will expire all others + if (isset($ans['rows']) and $ans['rows'] > 0) + $addr = $ans['addr:0']; else $addr = ''; $pg = settings($data, $user, $email, $addr, $err); diff --git a/src/ckdb.h b/src/ckdb.h index ee70868e..d8ae51de 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.731" +#define CKDB_VERSION DB_VERSION"-0.740" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -751,17 +751,21 @@ typedef struct paymentaddresses { char payaddress[TXT_BIG+1]; int32_t payratio; HISTORYDATECONTROLFIELDS; + bool match; // Non-db field } PAYMENTADDRESSES; #define ALLOC_PAYMENTADDRESSES 1024 #define LIMIT_PAYMENTADDRESSES 0 #define INIT_PAYMENTADDRESSES(_item) INIT_GENERIC(_item, paymentaddresses) #define DATA_PAYMENTADDRESSES(_var, _item) DATA_GENERIC(_var, _item, paymentaddresses, true) +#define DATA_PAYMENTADDRESSES_NULL(_var, _item) DATA_GENERIC(_var, _item, paymentaddresses, false) extern K_TREE *paymentaddresses_root; extern K_LIST *paymentaddresses_free; extern K_STORE *paymentaddresses_store; +#define PAYRATIODEF 1000000 + // PAYMENTS typedef struct payments { int64_t paymentid; @@ -1553,7 +1557,8 @@ extern K_ITEM *new_default_worker(PGconn *conn, bool update, int64_t userid, cha char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root); extern void dsp_paymentaddresses(K_ITEM *item, FILE *stream); extern cmp_t cmp_paymentaddresses(K_ITEM *a, K_ITEM *b); -extern K_ITEM *find_paymentaddresses(int64_t userid); +extern K_ITEM *find_paymentaddresses(int64_t userid, K_TREE_CTX *ctx); +extern K_ITEM *find_one_payaddress(int64_t userid, char *payaddress, K_TREE_CTX *ctx); extern cmp_t cmp_payments(K_ITEM *a, K_ITEM *b); extern cmp_t cmp_optioncontrol(K_ITEM *a, K_ITEM *b); extern K_ITEM *find_optioncontrol(char *optionname, tv_t *now); @@ -1695,9 +1700,9 @@ extern bool workers_update(PGconn *conn, K_ITEM *item, char *difficultydefault, char *idlenotificationtime, char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root, bool check); extern bool workers_fill(PGconn *conn); -extern K_ITEM *paymentaddresses_set(PGconn *conn, int64_t userid, char *payaddress, - char *by, char *code, char *inet, tv_t *cd, - K_TREE *trf_root); +extern bool paymentaddresses_set(PGconn *conn, int64_t userid, K_LIST *pa_store, + char *by, char *code, char *inet, tv_t *cd, + K_TREE *trf_root); extern bool paymentaddresses_fill(PGconn *conn); extern bool payments_fill(PGconn *conn); extern bool idcontrol_add(PGconn *conn, char *idname, char *idvalue, char *by, diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index 0d443b31..d175aea6 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -161,16 +161,21 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, __maybe_unused char *code, __maybe_unused char *inet, __maybe_unused tv_t *notcd, K_TREE *trf_root) { - K_ITEM *i_username, *i_passwordhash, *i_address, *i_email, *u_item, *pa_item; - char *email, *address; + K_ITEM *i_username, *i_passwordhash, *i_rows, *i_address, *i_ratio; + K_ITEM *i_email, *u_item, *pa_item; + char *email; char reply[1024] = ""; size_t siz = sizeof(reply); char tmp[1024]; PAYMENTADDRESSES *paymentaddresses; + K_STORE *pa_store = NULL; + K_TREE_CTX ctx[1]; USERS *users; char *reason = NULL; char *answer = NULL; size_t len, off; + int32_t ratio; + int rows, i; bool ok; LOGDEBUG("%s(): cmd '%s'", __func__, cmd); @@ -206,18 +211,35 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, APPEND_REALLOC(answer, off, len, tmp); K_RLOCK(paymentaddresses_free); - pa_item = find_paymentaddresses(users->userid); - K_RUNLOCK(paymentaddresses_free); - + pa_item = find_paymentaddresses(users->userid, ctx); if (pa_item) { DATA_PAYMENTADDRESSES(paymentaddresses, pa_item); - snprintf(tmp, sizeof(tmp), "addr=%s", - paymentaddresses->payaddress); - APPEND_REALLOC(answer, off, len, tmp); + while (pa_item && CURRENT(&(paymentaddresses->expirydate)) && + paymentaddresses->userid == users->userid) { + snprintf(tmp, sizeof(tmp), "addr:%d=%s%c", + rows, paymentaddresses->payaddress, FLDSEP); + APPEND_REALLOC(answer, off, len, tmp); + snprintf(tmp, sizeof(tmp), "ratio:%d=%d%c", + rows, paymentaddresses->payratio, FLDSEP); + APPEND_REALLOC(answer, off, len, tmp); + rows++; + + pa_item = prev_in_ktree(ctx); + DATA_PAYMENTADDRESSES_NULL(paymentaddresses, pa_item); + } + K_RUNLOCK(paymentaddresses_free); } else { - snprintf(tmp, sizeof(tmp), "addr="); - APPEND_REALLOC(answer, off, len, tmp); + K_RUNLOCK(paymentaddresses_free); + rows = 0; } + + snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c", + rows, FLDSEP, + "addr,ratio", FLDSEP); + APPEND_REALLOC(answer, off, len, tmp); + snprintf(tmp, sizeof(tmp), "arn=%s%carp=%s", + "PaymentAddresses", FLDSEP, ""); + APPEND_REALLOC(answer, off, len, tmp); } else { if (!check_hash(users, transfer_data(i_passwordhash))) { reason = "Incorrect password"; @@ -235,29 +257,83 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, } email = NULL; } - i_address = optional_name(trf_root, "address", - ADDR_MIN_LEN, - (char *)addrpatt, - reply, siz); - if (i_address) - address = transfer_data(i_address); - else { - if (*reply) { - reason = "Invalid address"; + + // address rows + i_rows = optional_name(trf_root, "rows", + 1, (char *)intpatt, + reply, siz); + if (!i_rows && *reply) { + // Exists, but invalid + reason = "System error"; + goto struckout; + } + if (i_rows) { + rows = atoi(transfer_data(i_rows)); + if (rows < 0) { + reason = "System error"; goto struckout; } - address = NULL; + if (rows > 0) { + pa_store = k_new_store(paymentaddresses_free); + K_WLOCK(paymentaddresses_free); + for (i = 0; i < rows; i++) { + snprintf(tmp, sizeof(tmp), "ratio:%d", i); + i_ratio = optional_name(trf_root, tmp, + 1, (char *)intpatt, + reply, siz); + if (*reply) { + K_WUNLOCK(paymentaddresses_free); + reason = "Invalid ratio"; + goto struckout; + } + if (i_ratio) + ratio = atoi(transfer_data(i_ratio)); + else + ratio = PAYRATIODEF; + + /* 0 = expire/remove the address + * intpatt means it will be >= 0 */ + if (ratio == 0) + continue; + + snprintf(tmp, sizeof(tmp), "address:%d", i); + i_address = require_name(trf_root, tmp, + ADDR_MIN_LEN, + (char *)addrpatt, + reply, siz); + if (!i_address) { + K_WUNLOCK(paymentaddresses_free); + reason = "Invalid address"; + goto struckout; + } + pa_item = k_unlink_head(paymentaddresses_free); + DATA_PAYMENTADDRESSES(paymentaddresses, pa_item); + bzero(paymentaddresses, sizeof(*paymentaddresses)); + STRNCPY(paymentaddresses->payaddress, + transfer_data(i_address)); + paymentaddresses->payratio = ratio; + k_add_head(pa_store, pa_item); + } + K_WUNLOCK(paymentaddresses_free); + } } + /* If all addresses have a ratio of zero + * pa_store->count will be 0 */ if ((email == NULL || *email == '\0') && - (address == NULL || *address == '\0')) { + (pa_store == NULL || pa_store->count == 0)) { reason = "Missing/Invalid value"; goto struckout; } - if (address && *address) { - if (!btc_valid_address(address)) { - reason = "Invalid BTC address"; - goto struckout; + if (pa_store && pa_store->count > 0) { + pa_item = pa_store->head; + while (pa_item) { + DATA_PAYMENTADDRESSES(paymentaddresses, pa_item); + if (!btc_valid_address(paymentaddresses->payaddress)) { + reason = "Invalid BTC address"; + goto struckout; + } + pa_item = pa_item->next; } } @@ -274,9 +350,9 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, } } - if (address && *address) { + if (pa_store && pa_store->count > 0) { ok = paymentaddresses_set(conn, users->userid, - address, by, + pa_store, by, code, inet, now, trf_root); if (!ok) { @@ -289,6 +365,15 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, } struckout: + if (pa_store) { + if (pa_store->count) { + K_WLOCK(paymentaddresses_free); + k_list_transfer_to_head(pa_store, paymentaddresses_free); + K_WUNLOCK(paymentaddresses_free); + } + k_free_store(pa_store); + pa_store = NULL; + } if (reason) { snprintf(reply, siz, "ERR.%s", reason); LOGERR("%s.%s.%s", cmd, id, reply); @@ -2988,7 +3073,7 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, int64_t ss_count, wm_count, ms_count; char tv_buf[DATE_BUFSIZ]; tv_t cd, begin_tv, block_tv, end_tv; - K_TREE_CTX ctx[1], wm_ctx[1], ms_ctx[1]; + K_TREE_CTX ctx[1], wm_ctx[1], ms_ctx[1], pay_ctx[1]; double ndiff, total_diff, elapsed; double diff_times = 1.0; double diff_add = 0.0; @@ -3276,7 +3361,8 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, PAYMENTADDRESSES *pa; char *payaddress; - pa_item = find_paymentaddresses(miningpayouts->userid); + // TODO: handle multiple addresses for one user + pa_item = find_paymentaddresses(miningpayouts->userid, pay_ctx); if (pa_item) { DATA_PAYMENTADDRESSES(pa, pa_item); payaddress = pa->payaddress; diff --git a/src/ckdb_data.c b/src/ckdb_data.c index 9657176c..8c17cd35 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -1108,35 +1108,37 @@ void dsp_paymentaddresses(K_ITEM *item, FILE *stream) } } -// order by userid asc,expirydate desc,payaddress asc +// order by expirydate asc,userid asc,payaddress asc cmp_t cmp_paymentaddresses(K_ITEM *a, K_ITEM *b) { PAYMENTADDRESSES *pa, *pb; DATA_PAYMENTADDRESSES(pa, a); DATA_PAYMENTADDRESSES(pb, b); - cmp_t c = CMP_BIGINT(pa->userid, pb->userid); + cmp_t c = CMP_TV(pa->expirydate, pb->expirydate); if (c == 0) { - c = CMP_TV(pb->expirydate, pa->expirydate); + c = CMP_BIGINT(pa->userid, pb->userid); if (c == 0) c = CMP_STR(pa->payaddress, pb->payaddress); } return c; } -// Only one for now ... -K_ITEM *find_paymentaddresses(int64_t userid) +/* Find the last CURRENT paymentaddresses for the given userid + * N.B. there can be more than one + * any more will be prev_in_ktree(ctx): CURRENT and userid matches */ +K_ITEM *find_paymentaddresses(int64_t userid, K_TREE_CTX *ctx) { PAYMENTADDRESSES paymentaddresses, *pa; - K_TREE_CTX ctx[1]; K_ITEM look, *item; - paymentaddresses.userid = userid; + paymentaddresses.expirydate.tv_sec = default_expiry.tv_sec; + paymentaddresses.expirydate.tv_usec = default_expiry.tv_usec; + paymentaddresses.userid = userid+1; paymentaddresses.payaddress[0] = '\0'; - paymentaddresses.expirydate.tv_sec = DATE_S_EOT; INIT_PAYMENTADDRESSES(&look); look.data = (void *)(&paymentaddresses); - item = find_after_in_ktree(paymentaddresses_root, &look, cmp_paymentaddresses, ctx); + item = find_before_in_ktree(paymentaddresses_root, &look, cmp_paymentaddresses, ctx); if (item) { DATA_PAYMENTADDRESSES(pa, item); if (pa->userid == userid && CURRENT(&(pa->expirydate))) @@ -1147,6 +1149,21 @@ K_ITEM *find_paymentaddresses(int64_t userid) return NULL; } +K_ITEM *find_one_payaddress(int64_t userid, char *payaddress, K_TREE_CTX *ctx) +{ + PAYMENTADDRESSES paymentaddresses; + K_ITEM look; + + paymentaddresses.expirydate.tv_sec = default_expiry.tv_sec; + paymentaddresses.expirydate.tv_usec = default_expiry.tv_usec; + paymentaddresses.userid = userid; + STRNCPY(paymentaddresses.payaddress, payaddress); + + INIT_PAYMENTADDRESSES(&look); + look.data = (void *)(&paymentaddresses); + return find_in_ktree(paymentaddresses_root, &look, cmp_paymentaddresses, ctx); +} + // order by userid asc,paydate asc,payaddress asc,expirydate desc cmp_t cmp_payments(K_ITEM *a, K_ITEM *b) { diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c index 471d5137..42be0f87 100644 --- a/src/ckdb_dbio.c +++ b/src/ckdb_dbio.c @@ -1427,56 +1427,44 @@ bool workers_fill(PGconn *conn) return ok; } -/* Whatever the current paymentaddresses are, replace them with this one - * Code allows for zero, one or more current payment address - * even though there currently can only be zero or one */ -K_ITEM *paymentaddresses_set(PGconn *conn, int64_t userid, char *payaddress, - char *by, char *code, char *inet, tv_t *cd, - K_TREE *trf_root) +/* Whatever the current paymentaddresses are, replace them with the list + * in pa_store + * Code allows for zero, one or more current payment address */ +bool paymentaddresses_set(PGconn *conn, int64_t userid, K_STORE *pa_store, + char *by, char *code, char *inet, tv_t *cd, + K_TREE *trf_root) { ExecStatusType rescode; bool conned = false; PGresult *res; K_TREE_CTX ctx[1]; - K_ITEM *item, *old, *this, look; - PAYMENTADDRESSES *row, pa, *thispa; - char *upd, *ins; - bool ok = false; - char *params[4 + HISTORYDATECOUNT]; - int n, par = 0; + K_ITEM *item, *match, *next, *prev; + PAYMENTADDRESSES *row, *pa; + char *upd = NULL, *ins; + size_t len, off; + bool ok = false, first; + char *params[1002]; // Limit of 999 addresses per user + char tmp[1024]; + int n, par = 0, count, matches; LOGDEBUG("%s(): add", __func__); - K_WLOCK(paymentaddresses_free); - item = k_unlink_head(paymentaddresses_free); - K_WUNLOCK(paymentaddresses_free); - - DATA_PAYMENTADDRESSES(row, item); - - row->paymentaddressid = nextid(conn, "paymentaddressid", 1, - cd, by, code, inet); - if (row->paymentaddressid == 0) - goto unitem; - - row->userid = userid; - STRNCPY(row->payaddress, payaddress); - row->payratio = 1000000; - - HISTORYDATEINIT(row, cd, by, code, inet); - HISTORYDATETRANSFER(trf_root, row); + // Quick early abort + if (pa_store->count > 999) + return false; - upd = "update paymentaddresses set expirydate=$1 where userid=$2 and expirydate=$3"; - par = 0; - params[par++] = tv_to_buf(cd, NULL, 0); - params[par++] = bigint_to_buf(row->userid, NULL, 0); - params[par++] = tv_to_buf((tv_t *)&default_expiry, NULL, 0); - PARCHKVAL(par, 3, params); + /* Since we are merging the changes in rather than just + * replacing the db contents, lock the data for the duration + * of the update to ensure nothing else changes it */ + K_WLOCK(paymentaddresses_free); if (conn == NULL) { conn = dbconnect(); conned = true; } + /* This means the nextid updates will rollback on an error, but also + * means that it will lock the nextid record for the whole update */ res = PQexec(conn, "Begin", CKPQ_WRITE); rescode = PQresultStatus(res); if (!PGOK(rescode)) { @@ -1485,36 +1473,126 @@ K_ITEM *paymentaddresses_set(PGconn *conn, int64_t userid, char *payaddress, } PQclear(res); - res = PQexecParams(conn, upd, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE); - rescode = PQresultStatus(res); - PQclear(res); - if (!PGOK(rescode)) { - PGLOGERR("Update", rescode, conn); - goto rollback; + // First step - DB expire all the old/changed records in RAM + LOGDEBUG("%s(): Step 1 userid=%"PRId64, __func__, userid); + count = matches = 0; + APPEND_REALLOC_INIT(upd, off, len); + APPEND_REALLOC(upd, off, len, + "update paymentaddresses set expirydate=$1 where " + "userid=$2 and expirydate=$3 and payaddress in ("); + par = 0; + params[par++] = tv_to_buf(cd, NULL, 0); + params[par++] = bigint_to_buf(userid, NULL, 0); + params[par++] = tv_to_buf((tv_t *)&default_expiry, NULL, 0); + + first = true; + item = find_paymentaddresses(userid, ctx); + DATA_PAYMENTADDRESSES_NULL(row, item); + while (item && CURRENT(&(row->expirydate)) && row->userid == userid) { + /* This is only possible if the DB was directly updated with + * more than 999 records then reloaded (or a code bug) */ + if (++count > 999) + break; + + // Find the RAM record in pa_store + match = pa_store->head; + while (match) { + DATA_PAYMENTADDRESSES(pa, match); + if (strcmp(pa->payaddress, row->payaddress) == 0 && + pa->payratio == row->payratio) { + pa->match = true; // Don't store it + matches++; + break; + } + match = match->next; + } + if (!match) { + // No matching replacement, so expire 'row' + params[par++] = str_to_buf(row->payaddress, NULL, 0); + if (!first) + APPEND_REALLOC(upd, off, len, ","); + first = false; + snprintf(tmp, sizeof(tmp), "$%d", par); + APPEND_REALLOC(upd, off, len, tmp); + } + item = prev_in_ktree(ctx); + DATA_PAYMENTADDRESSES_NULL(row, item); } + LOGDEBUG("%s(): Step 1 par=%d count=%d matches=%d first=%s", __func__, + par, count, matches, first ? "true" : "false"); + // Too many, or none need expiring = don't do the update + if (count > 999 || first == true) { + for (n = 0; n < par; n++) + free(params[n]); + par = 0; + // Too many + if (count > 999) + goto rollback; + } else { + APPEND_REALLOC(upd, off, len, ")"); + PARCHKVAL(par, par, params); + res = PQexecParams(conn, upd, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE); + rescode = PQresultStatus(res); + PQclear(res); + if (!PGOK(rescode)) { + PGLOGERR("Update", rescode, conn); + goto rollback; + } - for (n = 0; n < par; n++) - free(params[n]); + LOGDEBUG("%s(): Step 1 expired %d", __func__, par-3); + + for (n = 0; n < par; n++) + free(params[n]); + par = 0; + } + // Second step - add the non-matching records to the DB + LOGDEBUG("%s(): Step 2", __func__); ins = "insert into paymentaddresses " "(paymentaddressid,userid,payaddress,payratio" HISTORYDATECONTROL ") values (" PQPARAM9 ")"; - par = 0; - params[par++] = bigint_to_buf(row->paymentaddressid, NULL, 0); - params[par++] = bigint_to_buf(row->userid, NULL, 0); - params[par++] = str_to_buf(row->payaddress, NULL, 0); - params[par++] = int_to_buf(row->payratio, NULL, 0); - HISTORYDATEPARAMS(params, par, row); - PARCHK(par, params); + count = 0; + match = pa_store->head; + while (match) { + DATA_PAYMENTADDRESSES(row, match); + if (!row->match) { + row->paymentaddressid = nextid(conn, "paymentaddressid", 1, + cd, by, code, inet); + if (row->paymentaddressid == 0) + goto rollback; - res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE); - rescode = PQresultStatus(res); - PQclear(res); - if (!PGOK(rescode)) { - PGLOGERR("Insert", rescode, conn); - goto rollback; + row->userid = userid; + + HISTORYDATEINIT(row, cd, by, code, inet); + HISTORYDATETRANSFER(trf_root, row); + + par = 0; + params[par++] = bigint_to_buf(row->paymentaddressid, NULL, 0); + params[par++] = bigint_to_buf(row->userid, NULL, 0); + params[par++] = str_to_buf(row->payaddress, NULL, 0); + params[par++] = int_to_buf(row->payratio, NULL, 0); + HISTORYDATEPARAMS(params, par, row); + PARCHKVAL(par, 9, params); // As per PQPARAM9 above + + res = PQexecParams(conn, ins, par, NULL, (const char **)params, + NULL, NULL, 0, CKPQ_WRITE); + rescode = PQresultStatus(res); + PQclear(res); + if (!PGOK(rescode)) { + PGLOGERR("Insert", rescode, conn); + goto rollback; + } + + for (n = 0; n < par; n++) + free(params[n]); + par = 0; + + count++; + } + match = match->next; } + LOGDEBUG("%s(): Step 2 inserted %d", __func__, count); ok = true; rollback: @@ -1529,46 +1607,62 @@ unparam: PQfinish(conn); for (n = 0; n < par; n++) free(params[n]); -unitem: - K_WLOCK(paymentaddresses_free); - if (!ok) - k_add_head(paymentaddresses_free, item); - else { - // Change the expiry on all the old ones - pa.userid = userid; - pa.expirydate.tv_sec = DATE_S_EOT; - pa.payaddress[0] = '\0'; - INIT_PAYMENTADDRESSES(&look); - look.data = (void *)(&pa); - // Tree order is expirydate desc - old = find_after_in_ktree(paymentaddresses_root, &look, - cmp_paymentaddresses, ctx); - while (old) { - this = old; - DATA_PAYMENTADDRESSES(thispa, this); - if (thispa->userid != userid) - break; - old = next_in_ktree(ctx); - /* Tree remove+add below doesn't matter since - * this test will avoid reprocessing */ - if (CURRENT(&(thispa->expirydate))) { - paymentaddresses_root = remove_from_ktree(paymentaddresses_root, this, + FREENULL(upd); + // Third step - do step 1 and 2 to the RAM version of the DB + LOGDEBUG("%s(): Step 3, ok=%s", __func__, ok ? "true" : "false"); + matches = count = n = 0; + if (ok) { + // Change the expiry on all records that we expired in the DB + item = find_paymentaddresses(userid, ctx); + DATA_PAYMENTADDRESSES_NULL(row, item); + while (item && CURRENT(&(row->expirydate)) && row->userid == userid) { + prev = prev_in_ktree(ctx); + // Find the RAM record in pa_store + match = pa_store->head; + while (match) { + DATA_PAYMENTADDRESSES(pa, match); + if (strcmp(pa->payaddress, row->payaddress) == 0 && + pa->payratio == row->payratio) { + break; + } + match = match->next; + } + if (match) + matches++; + else { + // It wasn't a match, thus it was expired + n++; + paymentaddresses_root = remove_from_ktree(paymentaddresses_root, item, cmp_paymentaddresses); - copy_tv(&(thispa->expirydate), cd); - paymentaddresses_root = add_to_ktree(paymentaddresses_root, this, + copy_tv(&(row->expirydate), cd); + paymentaddresses_root = add_to_ktree(paymentaddresses_root, item, cmp_paymentaddresses); } + item = prev; + DATA_PAYMENTADDRESSES_NULL(row, item); + } + + // Add in all the non-matching ps_store + match = pa_store->head; + while (match) { + next = match->next; + DATA_PAYMENTADDRESSES(pa, match); + if (!pa->match) { + paymentaddresses_root = add_to_ktree(paymentaddresses_root, match, + cmp_paymentaddresses); + k_unlink_item(pa_store, match); + k_add_head(paymentaddresses_store, match); + count++; + } + match = next; } - paymentaddresses_root = add_to_ktree(paymentaddresses_root, item, - cmp_paymentaddresses); - k_add_head(paymentaddresses_store, item); } + LOGDEBUG("%s(): Step 3, untouched %d expired %d added %d", __func__, matches, n, count); + K_WUNLOCK(paymentaddresses_free); - if (ok) - return item; - else - return NULL; + // Calling function must clean up anything left in pa_store + return ok; } bool paymentaddresses_fill(PGconn *conn)