|
|
|
@ -1427,10 +1427,10 @@ 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, |
|
|
|
|
/* 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) |
|
|
|
|
{ |
|
|
|
@ -1438,45 +1438,33 @@ K_ITEM *paymentaddresses_set(PGconn *conn, int64_t userid, char *payaddress,
|
|
|
|
|
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,6 +1473,64 @@ K_ITEM *paymentaddresses_set(PGconn *conn, int64_t userid, char *payaddress,
|
|
|
|
|
} |
|
|
|
|
PQclear(res); |
|
|
|
|
|
|
|
|
|
// 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); |
|
|
|
@ -1493,22 +1539,44 @@ K_ITEM *paymentaddresses_set(PGconn *conn, int64_t userid, char *payaddress,
|
|
|
|
|
goto rollback; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 ")"; |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
PARCHK(par, params); |
|
|
|
|
PARCHKVAL(par, 9, params); // As per PQPARAM9 above
|
|
|
|
|
|
|
|
|
|
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE); |
|
|
|
|
res = PQexecParams(conn, ins, par, NULL, (const char **)params, |
|
|
|
|
NULL, NULL, 0, CKPQ_WRITE); |
|
|
|
|
rescode = PQresultStatus(res); |
|
|
|
|
PQclear(res); |
|
|
|
|
if (!PGOK(rescode)) { |
|
|
|
@ -1516,6 +1584,16 @@ K_ITEM *paymentaddresses_set(PGconn *conn, int64_t userid, char *payaddress,
|
|
|
|
|
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: |
|
|
|
|
if (ok) |
|
|
|
@ -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); |
|
|
|
|
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 { |
|
|
|
|
// 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, |
|
|
|
|
// 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); |
|
|
|
|
} |
|
|
|
|
paymentaddresses_root = add_to_ktree(paymentaddresses_root, 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_add_head(paymentaddresses_store, item); |
|
|
|
|
k_unlink_item(pa_store, match); |
|
|
|
|
k_add_head(paymentaddresses_store, match); |
|
|
|
|
count++; |
|
|
|
|
} |
|
|
|
|
match = next; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
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) |
|
|
|
|