New spi files

This commit is contained in:
Joseph 2023-03-27 07:23:27 -04:00
parent d83c2539bc
commit cb87b6f646
No known key found for this signature in database
GPG Key ID: E709789D3076B5CC
10 changed files with 2030 additions and 0 deletions

View File

@ -0,0 +1,477 @@
--
-- This file is part of TALER
-- Copyright (C) 2014--2022 Taler Systems SA
--
-- TALER is free software; you can redistribute it and/or modify it under the
-- terms of the GNU General Public License as published by the Free Software
-- Foundation; either version 3, or (at your option) any later version.
--
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along with
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
CREATE OR REPLACE FUNCTION exchange_do_batch4_known_coin(
IN in_coin_pub1 BYTEA,
IN in_denom_pub_hash1 BYTEA,
IN in_h_age_commitment1 BYTEA,
IN in_denom_sig1 BYTEA,
IN in_coin_pub2 BYTEA,
IN in_denom_pub_hash2 BYTEA,
IN in_h_age_commitment2 BYTEA,
IN in_denom_sig2 BYTEA,
IN in_coin_pub3 BYTEA,
IN in_denom_pub_hash3 BYTEA,
IN in_h_age_commitment3 BYTEA,
IN in_denom_sig3 BYTEA,
IN in_coin_pub4 BYTEA,
IN in_denom_pub_hash4 BYTEA,
IN in_h_age_commitment4 BYTEA,
IN in_denom_sig4 BYTEA,
OUT existed1 BOOLEAN,
OUT existed2 BOOLEAN,
OUT existed3 BOOLEAN,
OUT existed4 BOOLEAN,
OUT known_coin_id1 INT8,
OUT known_coin_id2 INT8,
OUT known_coin_id3 INT8,
OUT known_coin_id4 INT8,
OUT denom_pub_hash1 BYTEA,
OUT denom_pub_hash2 BYTEA,
OUT denom_pub_hash3 BYTEA,
OUT denom_pub_hash4 BYTEA,
OUT age_commitment_hash1 BYTEA,
OUT age_commitment_hash2 BYTEA,
OUT age_commitment_hash3 BYTEA,
OUT age_commitment_hash4 BYTEA)
LANGUAGE plpgsql
AS $$
BEGIN
WITH dd AS (
SELECT
denominations_serial,
coin_val, coin_frac
FROM denominations
WHERE denom_pub_hash
IN
(in_denom_pub_hash1,
in_denom_pub_hash2,
in_denom_pub_hash3,
in_denom_pub_hash4)
),--dd
input_rows AS (
VALUES
(in_coin_pub1,
in_denom_pub_hash1,
in_h_age_commitment1,
in_denom_sig1),
(in_coin_pub2,
in_denom_pub_hash2,
in_h_age_commitment2,
in_denom_sig2),
(in_coin_pub3,
in_denom_pub_hash3,
in_h_age_commitment3,
in_denom_sig3),
(in_coin_pub4,
in_denom_pub_hash4,
in_h_age_commitment4,
in_denom_sig4)
),--ir
ins AS (
INSERT INTO known_coins (
coin_pub,
denominations_serial,
age_commitment_hash,
denom_sig,
remaining_val,
remaining_frac
)
SELECT
ir.coin_pub,
dd.denominations_serial,
ir.age_commitment_hash,
ir.denom_sig,
dd.coin_val,
dd.coin_frac
FROM input_rows ir
JOIN dd
ON dd.denom_pub_hash = ir.denom_pub_hash
ON CONFLICT DO NOTHING
RETURNING known_coin_id
),--kc
exists AS (
SELECT
CASE
WHEN
ins.known_coin_id IS NOT NULL
THEN
FALSE
ELSE
TRUE
END AS existed,
ins.known_coin_id,
dd.denom_pub_hash,
kc.age_commitment_hash
FROM input_rows ir
LEFT JOIN ins
ON ins.coin_pub = ir.coin_pub
LEFT JOIN known_coins kc
ON kc.coin_pub = ir.coin_pub
LEFT JOIN dd
ON dd.denom_pub_hash = ir.denom_pub_hash
)--exists
SELECT
exists.existed AS existed1,
exists.known_coin_id AS known_coin_id1,
exists.denom_pub_hash AS denom_pub_hash1,
exists.age_commitment_hash AS age_commitment_hash1,
(
SELECT exists.existed
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash2
) AS existed2,
(
SELECT exists.known_coin_id
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash2
) AS known_coin_id2,
(
SELECT exists.denom_pub_hash
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash2
) AS denom_pub_hash2,
(
SELECT exists.age_commitment_hash
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash2
)AS age_commitment_hash2,
(
SELECT exists.existed
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash3
) AS existed3,
(
SELECT exists.known_coin_id
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash3
) AS known_coin_id3,
(
SELECT exists.denom_pub_hash
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash3
) AS denom_pub_hash3,
(
SELECT exists.age_commitment_hash
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash3
)AS age_commitment_hash3,
(
SELECT exists.existed
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash4
) AS existed4,
(
SELECT exists.known_coin_id
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash4
) AS known_coin_id4,
(
SELECT exists.denom_pub_hash
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash4
) AS denom_pub_hash4,
(
SELECT exists.age_commitment_hash
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash4
)AS age_commitment_hash4
FROM exists;
RETURN;
END $$;
CREATE OR REPLACE FUNCTION exchange_do_batch2_known_coin(
IN in_coin_pub1 BYTEA,
IN in_denom_pub_hash1 BYTEA,
IN in_h_age_commitment1 BYTEA,
IN in_denom_sig1 BYTEA,
IN in_coin_pub2 BYTEA,
IN in_denom_pub_hash2 BYTEA,
IN in_h_age_commitment2 BYTEA,
IN in_denom_sig2 BYTEA,
OUT existed1 BOOLEAN,
OUT existed2 BOOLEAN,
OUT known_coin_id1 INT8,
OUT known_coin_id2 INT8,
OUT denom_pub_hash1 BYTEA,
OUT denom_pub_hash2 BYTEA,
OUT age_commitment_hash1 BYTEA,
OUT age_commitment_hash2 BYTEA)
LANGUAGE plpgsql
AS $$
BEGIN
WITH dd AS (
SELECT
denominations_serial,
coin_val, coin_frac
FROM denominations
WHERE denom_pub_hash
IN
(in_denom_pub_hash1,
in_denom_pub_hash2)
),--dd
input_rows AS (
VALUES
(in_coin_pub1,
in_denom_pub_hash1,
in_h_age_commitment1,
in_denom_sig1),
(in_coin_pub2,
in_denom_pub_hash2,
in_h_age_commitment2,
in_denom_sig2)
),--ir
ins AS (
INSERT INTO known_coins (
coin_pub,
denominations_serial,
age_commitment_hash,
denom_sig,
remaining_val,
remaining_frac
)
SELECT
ir.coin_pub,
dd.denominations_serial,
ir.age_commitment_hash,
ir.denom_sig,
dd.coin_val,
dd.coin_frac
FROM input_rows ir
JOIN dd
ON dd.denom_pub_hash = ir.denom_pub_hash
ON CONFLICT DO NOTHING
RETURNING known_coin_id
),--kc
exists AS (
SELECT
CASE
WHEN ins.known_coin_id IS NOT NULL
THEN
FALSE
ELSE
TRUE
END AS existed,
ins.known_coin_id,
dd.denom_pub_hash,
kc.age_commitment_hash
FROM input_rows ir
LEFT JOIN ins
ON ins.coin_pub = ir.coin_pub
LEFT JOIN known_coins kc
ON kc.coin_pub = ir.coin_pub
LEFT JOIN dd
ON dd.denom_pub_hash = ir.denom_pub_hash
)--exists
SELECT
exists.existed AS existed1,
exists.known_coin_id AS known_coin_id1,
exists.denom_pub_hash AS denom_pub_hash1,
exists.age_commitment_hash AS age_commitment_hash1,
(
SELECT exists.existed
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash2
) AS existed2,
(
SELECT exists.known_coin_id
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash2
) AS known_coin_id2,
(
SELECT exists.denom_pub_hash
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash2
) AS denom_pub_hash2,
(
SELECT exists.age_commitment_hash
FROM exists
WHERE exists.denom_pub_hash = in_denom_pub_hash2
)AS age_commitment_hash2
FROM exists;
RETURN;
END $$;
CREATE OR REPLACE FUNCTION exchange_do_batch1_known_coin(
IN in_coin_pub1 BYTEA,
IN in_denom_pub_hash1 BYTEA,
IN in_h_age_commitment1 BYTEA,
IN in_denom_sig1 BYTEA,
OUT existed1 BOOLEAN,
OUT known_coin_id1 INT8,
OUT denom_pub_hash1 BYTEA,
OUT age_commitment_hash1 BYTEA)
LANGUAGE plpgsql
AS $$
BEGIN
WITH dd AS (
SELECT
denominations_serial,
coin_val, coin_frac
FROM denominations
WHERE denom_pub_hash
IN
(in_denom_pub_hash1,
in_denom_pub_hash2)
),--dd
input_rows AS (
VALUES
(in_coin_pub1,
in_denom_pub_hash1,
in_h_age_commitment1,
in_denom_sig1)
),--ir
ins AS (
INSERT INTO known_coins (
coin_pub,
denominations_serial,
age_commitment_hash,
denom_sig,
remaining_val,
remaining_frac
)
SELECT
ir.coin_pub,
dd.denominations_serial,
ir.age_commitment_hash,
ir.denom_sig,
dd.coin_val,
dd.coin_frac
FROM input_rows ir
JOIN dd
ON dd.denom_pub_hash = ir.denom_pub_hash
ON CONFLICT DO NOTHING
RETURNING known_coin_id
),--kc
exists AS (
SELECT
CASE
WHEN ins.known_coin_id IS NOT NULL
THEN
FALSE
ELSE
TRUE
END AS existed,
ins.known_coin_id,
dd.denom_pub_hash,
kc.age_commitment_hash
FROM input_rows ir
LEFT JOIN ins
ON ins.coin_pub = ir.coin_pub
LEFT JOIN known_coins kc
ON kc.coin_pub = ir.coin_pub
LEFT JOIN dd
ON dd.denom_pub_hash = ir.denom_pub_hash
)--exists
SELECT
exists.existed AS existed1,
exists.known_coin_id AS known_coin_id1,
exists.denom_pub_hash AS denom_pub_hash1,
exists.age_commitment_hash AS age_commitment_hash1
FROM exists;
RETURN;
END $$;
/*** THIS SQL CODE WORKS ***/
/*
CREATE OR REPLACE FUNCTION exchange_do_batch2_known_coin(
IN in_coin_pub1 BYTEA,
IN in_denom_pub_hash1 TEXT,
IN in_h_age_commitment1 TEXT,
IN in_denom_sig1 TEXT,
IN in_coin_pub2 BYTEA,
IN in_denom_pub_hash2 TEXT,
IN in_h_age_commitment2 TEXT,
IN in_denom_sig2 TEXT,
OUT existed1 BOOLEAN,
OUT existed2 BOOLEAN,
OUT known_coin_id1 INT8,
OUT known_coin_id2 INT8,
OUT denom_pub_hash1 TEXT,
OUT denom_pub_hash2 TEXT,
OUT age_commitment_hash1 TEXT,
OUT age_commitment_hash2 TEXT)
LANGUAGE plpgsql
AS $$
DECLARE
ins_values RECORD;
BEGIN
FOR i IN 1..2 LOOP
ins_values := (
SELECT
in_coin_pub1 AS coin_pub,
in_denom_pub_hash1 AS denom_pub_hash,
in_h_age_commitment1 AS age_commitment_hash,
in_denom_sig1 AS denom_sig
WHERE i = 1
UNION
SELECT
in_coin_pub2 AS coin_pub,
in_denom_pub_hash2 AS denom_pub_hash,
in_h_age_commitment2 AS age_commitment_hash,
in_denom_sig2 AS denom_sig
WHERE i = 2
);
WITH dd (denominations_serial, coin_val, coin_frac) AS (
SELECT denominations_serial, coin_val, coin_frac
FROM denominations
WHERE denom_pub_hash = ins_values.denom_pub_hash
),
input_rows(coin_pub) AS (
VALUES (ins_values.coin_pub)
),
ins AS (
INSERT INTO known_coins (
coin_pub,
denominations_serial,
age_commitment_hash,
denom_sig,
remaining_val,
remaining_frac
) SELECT
input_rows.coin_pub,
dd.denominations_serial,
ins_values.age_commitment_hash,
ins_values.denom_sig,
coin_val,
coin_frac
FROM dd
CROSS JOIN input_rows
ON CONFLICT DO NOTHING
RETURNING known_coin_id, denom_pub_hash
)
SELECT
CASE i
WHEN 1 THEN
COALESCE(ins.known_coin_id, 0) <> 0 AS existed1,
ins.known_coin_id AS known_coin_id1,
ins.denom_pub_hash AS denom_pub_hash1,
ins.age_commitment_hash AS age_commitment_hash1
WHEN 2 THEN
COALESCE(ins.known_coin_id, 0) <> 0 AS existed2,
ins.known_coin_id AS known_coin_id2,
ins.denom_pub_hash AS denom_pub_hash2,
ins.age_commitment_hash AS age_commitment_hash2
END
FROM ins;
END LOOP;
END;
$$;*/

View File

@ -0,0 +1,60 @@
--
-- This file is part of TALER
-- Copyright (C) 2014--2022 Taler Systems SA
--
-- TALER is free software; you can redistribute it and/or modify it under the
-- terms of the GNU General Public License as published by the Free Software
-- Foundation; either version 3, or (at your option) any later version.
--
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along with
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
--
CREATE OR REPLACE FUNCTION exchange_do_get_ready_deposit(
IN in_now INT8,
IN in_start_shard_now INT8,
IN in_end_shard_now INT8,
OUT out_payto_uri VARCHAR,
OUT out_merchant_pub BYTEA
)
LANGUAGE plpgsql
AS $$
DECLARE
curs CURSOR
FOR
SELECT
coin_pub
,deposit_serial_id
,wire_deadline
,shard
FROM deposits_by_ready dbr
WHERE wire_deadline <= in_now
AND shard >= in_start_shard_now
AND shard <=in_end_shard_now
ORDER BY
wire_deadline ASC
,shard ASC
LIMIT 1;
DECLARE
i RECORD;
BEGIN
OPEN curs;
FETCH FROM curs INTO i;
SELECT
payto_uri
,merchant_pub
INTO
out_payto_uri
,out_merchant_pub
FROM deposits
JOIN wire_targets wt
USING (wire_target_h_payto)
WHERE
i.coin_pub = coin_pub
AND i.deposit_serial_id=deposit_serial_id;
CLOSE curs;
RETURN;
END $$;

View File

@ -0,0 +1,41 @@
Server Programming Interface (SPI)
Dependencies:
=============
These are the direct dependencies for running SPI functions :
Step 1:
"postgresql-server-dev-<depends on your postgresql version>"
-- sudo apt-get install libpq-dev postgresql-server-dev-13
Step 2:
To solve gssapi/gssapi.h, use the following command:
apt-get install libkrb5-dev
Step 3:
apt-cache search openssl | grep -- -dev
apt-get install libssl-dev
Compile:
========
gcc -shared -o <file_name>.so <file_name>.c
CALL FUNCTIONS:
===============
psql -c "SELECT <function_name>();" db_name
Structure:
==========
usr/include/postgres/
usr/include/postgres/13/server/
make
make install
psql

Binary file not shown.

View File

@ -0,0 +1,818 @@
#include "postgres.h"
#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>
#include <internal/libpq-int.h>
#include <catalog/pg_type.h>
#include <executor/spi.h>
#include <funcapi.h>
#include <fmgr.h>
#include <utils/builtins.h>
#include "utils/array.h"
#include <sys/time.h>
#include "utils/numeric.h"
#include "utils/timestamp.h"
#include <utils/bytea.h>
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
typedef struct {
Datum col1;
Datum col2;
} valuest;
void _PG_init(void);
void _PG_fini(void);
void _PG_init(void)
{
}
PG_FUNCTION_INFO_V1(pg_spi_insert_int);
PG_FUNCTION_INFO_V1(pg_spi_select_from_x);
PG_FUNCTION_INFO_V1(pg_spi_select_pair_from_y);
//PG_FUNCTION_INFO_V1(pg_spi_select_with_cond);
PG_FUNCTION_INFO_V1(pg_spi_update_y);
PG_FUNCTION_INFO_V1(pg_spi_prepare_example);
PG_FUNCTION_INFO_V1(pg_spi_prepare_example_without_saveplan);
PG_FUNCTION_INFO_V1(pg_spi_prepare_insert);
PG_FUNCTION_INFO_V1(pg_spi_prepare_insert_without_saveplan);
//PG_FUNCTION_INFO_V1(pg_spi_prepare_select_with_cond);
PG_FUNCTION_INFO_V1(pg_spi_prepare_select_with_cond_without_saveplan);
PG_FUNCTION_INFO_V1(pg_spi_prepare_update);
PG_FUNCTION_INFO_V1(pg_spi_get_dep_ref_fees);
// SIMPLE SELECT
Datum
pg_spi_prepare_example(PG_FUNCTION_ARGS)
{
static SPIPlanPtr prepared_plan;
int ret;
int64 result;
char * value;
SPIPlanPtr new_plan;
ret=SPI_connect();
if (ret != SPI_OK_CONNECT) {
elog(ERROR, "DB connexion failed ! \n");
}
{
if (prepared_plan == NULL)
{
new_plan = SPI_prepare("SELECT 1 FROM joseph_test.X", 0, NULL);
prepared_plan = SPI_saveplan(new_plan);
if (prepared_plan == NULL)
{
elog(ERROR, "FAIL TO SAVE !\n");
}
}
ret = SPI_execute_plan(prepared_plan, NULL, 0,false, 0);
if (ret != SPI_OK_SELECT) {
elog(ERROR, "SELECT FAILED %d !\n", ret);
}
if (SPI_tuptable != NULL && SPI_tuptable->vals != NULL && SPI_tuptable->tupdesc != NULL)
{
value = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1);
result = atoi(value);
}
else
{
elog(ERROR, "EMPTY TABLE !\n");
}
}
SPI_finish();
PG_RETURN_INT64(result);
}
Datum
pg_spi_prepare_example_without_saveplan(PG_FUNCTION_ARGS)
{
int ret;
int64 result;
char * value;
SPIPlanPtr new_plan;
ret=SPI_connect();
if (ret != SPI_OK_CONNECT) {
elog(ERROR, "DB connexion failed ! \n");
}
{
new_plan = SPI_prepare("SELECT 1 FROM joseph_test.X", 0, NULL);
ret = SPI_execute_plan(new_plan, NULL, 0,false, 0);
if (ret != SPI_OK_SELECT) {
elog(ERROR, "SELECT FAILED %d !\n", ret);
}
if (SPI_tuptable != NULL
&& SPI_tuptable->vals != NULL
&& SPI_tuptable->tupdesc != NULL)
{
value = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1);
result = atoi(value);
}
else
{
elog(ERROR, "EMPTY TABLE !\n");
}
}
SPI_finish();
PG_RETURN_INT64(result);// PG_RETURN_INT64(result);
}
//SELECT 1 FROM X
//V1
Datum
pg_spi_select_from_x(PG_FUNCTION_ARGS)
{
int ret;
char *query = "SELECT 1 FROM joseph_test.X";
uint64 proc;
ret = SPI_connect();
if (ret != SPI_OK_CONNECT)
{
elog(ERROR, "SPI_connect failed");
}
ret = SPI_exec(query, 10);
proc = SPI_processed;
if (ret != SPI_OK_SELECT)
{
elog(ERROR, "SPI_exec failed");
}
SPI_finish();
PG_RETURN_INT64(proc);
}
//INSERT INTO X VALUES (1)
Datum
pg_spi_insert_int(PG_FUNCTION_ARGS)
{
int ret;
int nargs;
Oid argtypes[1];
Datum values[1];
char *query = "INSERT INTO joseph_test.X (a) VALUES ($1)";
ret = SPI_connect();
if (ret != SPI_OK_CONNECT)
{
elog(ERROR, "SPI_connect failed");
}
nargs = 1;
argtypes[0] = INT4OID;
values[0] = Int32GetDatum(3);
ret = SPI_execute_with_args(query, nargs, argtypes, values, NULL, false, 0);
if (ret != SPI_OK_INSERT)
{
elog(ERROR, "SPI_execute_with_args failed");
}
SPI_finish();
PG_RETURN_VOID();
}
Datum
pg_spi_prepare_insert(PG_FUNCTION_ARGS)
{
static SPIPlanPtr prepared_plan = NULL;
int ret;
int nargs;
Oid argtypes[1];
Datum values[1];
char *query = "INSERT INTO joseph_test.X (a) VALUES ($1)";
SPIPlanPtr new_plan;
ret = SPI_connect();
if (ret != SPI_OK_CONNECT)
{
elog(ERROR, "SPI_connect failed ! \n");
}
if (prepared_plan == NULL) {
argtypes[0] = INT4OID;
nargs = 1;
values[0] = Int32GetDatum(3);
new_plan = SPI_prepare(query, nargs, argtypes);
if (new_plan== NULL)
{
elog(ERROR, "SPI_prepare failed ! \n");
}
prepared_plan = SPI_saveplan(new_plan);
if (prepared_plan == NULL)
{
elog(ERROR, "SPI_saveplan failed ! \n");
}
}
ret = SPI_execute_plan(prepared_plan, values, NULL, false, 0);
if (ret != SPI_OK_INSERT)
{
elog(ERROR, "SPI_execute_plan failed ! \n");
}
SPI_finish();
PG_RETURN_VOID();
}
/*
Datum
pg_spi_prepare_insert_bytea(PG_FUNCTION_ARGS)
{
static SPIPlanPtr prepared_plan = NULL;
int ret;
int nargs;
Oid argtypes[1];
Datum values[1];
Oid argtypes2[1];
Datum val[1];
char *query = "INSERT INTO joseph_test.X (a) VALUES ($1)";
SPIPlanPtr new_plan;
ret = SPI_connect();
if (ret != SPI_OK_CONNECT)
{
elog(ERROR, "SPI_connect failed ! \n");
}
if (prepared_plan == NULL) {
argtypes2[0] = BOOLOID;
val[0] = BoolGetDatum();
argtypes[0] = BYTEAOID;
nargs = 1;
values[0] = Int32GetDatum(3);
new_plan = SPI_prepare(query, nargs, argtypes);
if (new_plan== NULL)
{
elog(ERROR, "SPI_prepare failed ! \n");
}
prepared_plan = SPI_saveplan(new_plan);
if (prepared_plan == NULL)
{
elog(ERROR, "SPI_saveplan failed ! \n");
}
}
ret = SPI_execute_plan(prepared_plan, values, NULL, false, 0);
if (ret != SPI_OK_INSERT)
{
elog(ERROR, "SPI_execute_plan failed ! \n");
}
SPI_finish();
PG_RETURN_VOID();
}
*/
Datum
pg_spi_prepare_insert_without_saveplan(PG_FUNCTION_ARGS)
{
int ret;
int nargs;
Oid argtypes[1];
Datum values[1];
char *query = "INSERT INTO joseph_test.X (a) VALUES ($1)";
SPIPlanPtr new_plan;
ret = SPI_connect();
if (ret != SPI_OK_CONNECT)
{
elog(ERROR, "SPI_connect failed");
}
{
argtypes[0] = INT4OID;
nargs = 1;
values[0] = Int32GetDatum(3);
new_plan = SPI_prepare(query, nargs, argtypes);
if (new_plan== NULL)
{
elog(ERROR, "SPI_prepare failed");
}
}
ret = SPI_execute_plan(new_plan, values, NULL, false, 0);
if (ret != SPI_OK_INSERT)
{
elog(ERROR, "SPI_execute_plan failed");
}
SPI_finish();
PG_RETURN_VOID();
}
/*
Datum
pg_spi_select_pair_from_y(PG_FUNCTION_ARGS)
{
int ret;
valuest result;
bool isnull;
char *query = "SELECT 1,1 FROM joseph_test.Y";
result.col1 = 0;
result.col2 = 0;
if ((ret = SPI_connect()) < 0) {
fprintf(stderr, "SPI_connect returned %d\n", ret);
exit(1);
}
ret = SPI_exec(query, 0);
if (ret == SPI_OK_SELECT && SPI_processed > 0) {
int i;
SPITupleTable *tuptable = SPI_tuptable;
TupleDesc tupdesc = tuptable->tupdesc;
for (i = 0; i < SPI_processed; i++) {
HeapTuple tuple = tuptable->vals[i];
result.col1 = SPI_getbinval(tuple, tupdesc, 1, &isnull);
result.col2 = SPI_getbinval(tuple, tupdesc, 2, &isnull);
}
}
SPI_finish();
PG_RETURN_TEXT_P(result);
}
*/
//SELECT X FROM Y WHERE Z=$1
/*
Datum
pg_spi_select_with_cond(PG_FUNCTION_ARGS)
{
int ret;
char *query;
int nargs;
Oid argtypes[1];
Datum values[1];
uint64 proc;
query = "SELECT col1 FROM joseph_test.Y WHERE col2 = $1";
ret = SPI_connect();
if (ret != SPI_OK_CONNECT) {
elog(ERROR, "SPI_connect failed: %d", ret);
}
nargs = 1;
argtypes[0] = INT4OID;
values[0] = Int32GetDatum(2);
ret = SPI_execute_with_args(query, nargs, argtypes, values, NULL, false, 0);
proc = SPI_processed;
if (ret != SPI_OK_SELECT)
{
elog(ERROR, "SPI_execute_with_args failed");
}
SPI_finish();
PG_RETURN_INT64(proc);
}*/
////////SELECT WITH COND
/*
Datum pg_spi_prepare_select_with_cond(PG_FUNCTION_ARGS) {
static SPIPlanPtr prepared_plan = NULL;
SPIPlanPtr new_plan;
int ret;
Datum values[1];
uint64 proc;
int nargs;
Oid argtypes[1];
char *query = "SELECT col1 FROM joseph_test.Y WHERE col1 = $1";
int result = 0;
ret = SPI_connect();
if (ret != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed ! \n");
if (prepared_plan == NULL) {
argtypes[0] = INT4OID;
nargs = 1;
values[0] = DatumGetByteaP(SPI_getbinval(tuptable->vals[0], tupdesc, 1, &isnull)); //Value col2
new_plan = SPI_prepare(query, nargs, argtypes);
if (new_plan == NULL)
elog(ERROR, "SPI_prepare failed ! \n");
prepared_plan = SPI_saveplan(new_plan);
if (prepared_plan == NULL)
elog(ERROR, "SPI_saveplan failed ! \n");
}
ret = SPI_execute_plan(prepared_plan, values, NULL, false, 0);
if (ret != SPI_OK_SELECT) {
elog(ERROR, "SPI_execute_plan failed: %d \n", ret);
}
proc = SPI_processed;
if (proc > 0) {
SPITupleTable *tuptable = SPI_tuptable;
TupleDesc tupdesc = tuptable->tupdesc;
HeapTuple tuple;
int i;
for (i = 0; i < proc; i++) {
tuple = tuptable->vals[i];
for (int j = 1; j <= tupdesc->natts; j++) {
char * value = SPI_getvalue(tuple, tupdesc, j);
result += atoi(value);
}
}
}
SPI_finish();
PG_RETURN_INT64(result);
}
*/
Datum pg_spi_prepare_select_with_cond_without_saveplan(PG_FUNCTION_ARGS) {
SPIPlanPtr new_plan;
int ret;
Datum values[1];
uint64 proc;
int nargs;
Oid argtypes[1];
char *query = "SELECT col1 FROM joseph_test.Y WHERE col2 = $1";
int result = 0;
ret = SPI_connect();
if (ret != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed ! \n");
{
argtypes[0] = INT4OID;
nargs = 1;
values[0] = Int32GetDatum(2); //Value col2
new_plan = SPI_prepare(query, nargs, argtypes);
if (new_plan == NULL)
elog(ERROR, "SPI_prepare failed ! \n");
}
ret = SPI_execute_plan(new_plan, values, NULL, false, 0);
if (ret != SPI_OK_SELECT) {
elog(ERROR, "SPI_execute_plan failed: %d \n", ret);
}
proc = SPI_processed;
if (proc > 0) {
SPITupleTable *tuptable = SPI_tuptable;
TupleDesc tupdesc = tuptable->tupdesc;
HeapTuple tuple;
int i;
for (i = 0; i < proc; i++) {
tuple = tuptable->vals[i];
for (int j = 1; j <= tupdesc->natts; j++) {
char * value = SPI_getvalue(tuple, tupdesc, j);
result += atoi(value);
}
}
}
SPI_finish();
PG_RETURN_INT64(result);
}
Datum
pg_spi_update_y(PG_FUNCTION_ARGS)
{
int ret;
int nargs;
Oid argtypes[1];
Datum values[1];
char *query = "UPDATE joseph_test.Y SET col1 = 4 WHERE (col2 = $1)";
ret = SPI_connect();
if (ret != SPI_OK_CONNECT)
{
elog(ERROR, "SPI_connect failed ! \n");
}
nargs = 1;
argtypes[0] = INT4OID;
values[0] = Int32GetDatum(0);
ret = SPI_execute_with_args(query, nargs, argtypes, values, NULL, false, 0);
if (ret != SPI_OK_UPDATE)
{
elog(ERROR, "SPI_execute_with_args failed ! \n");
}
SPI_finish();
PG_RETURN_VOID();
}
Datum
pg_spi_prepare_update(PG_FUNCTION_ARGS)
{
static SPIPlanPtr prepared_plan = NULL;
SPIPlanPtr new_plan;
int ret;
int nargs;
Oid argtypes[1];
Datum values[1];
char *query = "UPDATE joseph_test.Y SET col1 = 4 WHERE (col2 = $1)";
ret = SPI_connect();
if (ret != SPI_OK_CONNECT)
{
elog(ERROR, "SPI_connect failed ! \n");
}
if ( prepared_plan == NULL)
{
argtypes[0] = INT4OID;
nargs = 1;
values[0] = Int32GetDatum(3);
//PREPARE
new_plan = SPI_prepare(query, nargs, argtypes);
if (new_plan == NULL)
elog(ERROR, "SPI_prepare failed ! \n");
//SAVEPLAN
prepared_plan = SPI_saveplan(new_plan);
if(prepared_plan == NULL)
elog(ERROR, "SPI_saveplan failed ! \n");
}
ret = SPI_execute_plan(prepared_plan, values, NULL, false, 0);
if (ret != SPI_OK_UPDATE)
elog(ERROR, "SPI_execute_plan failed ! \n");
SPI_finish();
PG_RETURN_VOID();
}
/*
Datum
pg_spi_prepare_update_without_saveplan(PG_FUNCTION_ARGS)
{}*/
void _PG_fini(void)
{
}
/*
*/
Datum
pg_spi_get_dep_ref_fees (PG_FUNCTION_ARGS) {
/* Define plan to save */
static SPIPlanPtr deposit_plan;
static SPIPlanPtr ref_plan;
static SPIPlanPtr fees_plan;
static SPIPlanPtr dummy_plan;
/* Define variables to update */
Timestamp refund_deadline = PG_GETARG_TIMESTAMP(0);
bytea *merchant_pub = PG_GETARG_BYTEA_P(1);
bytea *wire_target_h_payto = PG_GETARG_BYTEA_P(2);
bytea *wtid_raw = PG_GETARG_BYTEA_P(3);
bool is_null;
/* Define variables to store the results of each SPI query */
uint64_t sum_deposit_val = 0;
uint32_t sum_deposit_frac = 0;
uint64_t s_refund_val = 0;
uint32_t s_refund_frac = 0;
uint64_t sum_dep_fee_val = 0;
uint32_t sum_dep_fee_frac = 0;
uint64_t norm_refund_val = 0;
uint32_t norm_refund_frac = 0;
uint64_t sum_refund_val = 0;
uint32_t sum_refund_frac = 0;
/* Define variables to store the Tuptable */
SPITupleTable *dep_res;
SPITupleTable *ref_res;
SPITupleTable *ref_by_coin_res;
SPITupleTable *norm_ref_by_coin_res;
SPITupleTable *fully_refunded_coins_res;
SPITupleTable *fees_res;
SPITupleTable *dummys_res;
/* Define variable to update */
Datum values_refund[2];
Datum values_deposit[3];
Datum values_fees[2];
Datum values_dummys[2];
TupleDesc tupdesc;
/* Define variables to replace some tables */
bytea *ref_by_coin_coin_pub;
int64 ref_by_coin_deposit_serial_id = 0;
bytea *norm_ref_by_coin_coin_pub;
int64_t norm_ref_by_coin_deposit_serial_id = 0;
bytea *new_dep_coin_pub = NULL;
int res = SPI_connect();
/* Connect to SPI */
if (res < 0) {
elog(ERROR, "Could not connect to SPI manager");
}
if (deposit_plan == NULL)
{
const char *dep_sql;
SPIPlanPtr new_plan;
// Execute first query and store results in variables
dep_sql =
"UPDATE deposits SET done=TRUE "
"WHERE NOT (done OR policy_blocked) "
"AND refund_deadline=$1 "
"AND merchant_pub=$2 "
"AND wire_target_h_payto=$3 "
"RETURNING "
"deposit_serial_id,"
"coin_pub,"
"amount_with_fee_val,"
"amount_with_fee_frac;";
fprintf(stderr, "dep sql %d\n", 1);
new_plan =
SPI_prepare(dep_sql, 4,(Oid[]){INT8OID, BYTEAOID, BYTEAOID});
fprintf(stderr, "dep sql %d\n", 2);
if (new_plan == NULL)
elog(ERROR, "SPI_prepare failed for dep \n");
deposit_plan = SPI_saveplan(new_plan);
if (deposit_plan == NULL)
elog(ERROR, "SPI_saveplan failed for dep \n");
}
fprintf(stdout, "dep sql %d\n", 3);
values_deposit[0] = Int64GetDatum(refund_deadline);
values_deposit[1] = PointerGetDatum(merchant_pub);
values_deposit[2] = PointerGetDatum(wire_target_h_payto);
res = SPI_execute_plan (deposit_plan,
values_deposit,
NULL,
true,
0);
fprintf(stdout, "dep sql %d\n", 4);
if (res != SPI_OK_UPDATE)
{
elog(ERROR, "Failed to execute subquery 1 \n");
}
// STORE TUPTABLE deposit
dep_res = SPI_tuptable;
for (unsigned int i = 0; i < SPI_processed; i++) {
int64 dep_deposit_serial_ids = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &is_null));
bytea *dep_coin_pub = DatumGetByteaP(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &is_null));
int64 dep_amount_val = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 3, &is_null));
int32 dep_amount_frac = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 4, &is_null));
if (is_null)
elog(ERROR, "Failed to retrive data from deposit \n");
if (ref_plan == NULL)
{
// Execute second query with parameters from first query and store results in variables
const char * ref_sql =
"SELECT amount_with_fee_val, amount_with_fee_frac, coin_pub, deposit_serial_id "
"FROM refunds "
"WHERE coin_pub=$1 "
"AND deposit_serial_id=$2;";
SPIPlanPtr new_plan = SPI_prepare(ref_sql, 3, (Oid[]){BYTEAOID, INT8OID});
if (new_plan == NULL)
elog(ERROR, "SPI_prepare failed for refund\n");
ref_plan = SPI_saveplan(new_plan);
if (ref_plan == NULL)
elog(ERROR, "SPI_saveplan failed for refund\n");
}
values_refund[0] = PointerGetDatum(dep_coin_pub);
values_refund[1] = Int64GetDatum(dep_deposit_serial_ids);
res = SPI_execute_plan(ref_plan,
values_refund,
NULL,
false,
0);
if (res != SPI_OK_SELECT)
elog(ERROR, "Failed to execute subquery 2\n");
// STORE TUPTABLE refund
ref_res = SPI_tuptable;
for (unsigned int j = 0; j < SPI_processed; j++) {
int64 ref_refund_val = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[j], SPI_tuptable->tupdesc, 1, &is_null));
int32 ref_refund_frac = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[j], SPI_tuptable->tupdesc, 2, &is_null));
bytea *ref_coin_pub = DatumGetByteaP(SPI_getbinval(SPI_tuptable->vals[j], SPI_tuptable->tupdesc, 3, &is_null));
int64 ref_deposit_serial_id = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[j], SPI_tuptable->tupdesc, 4, &is_null));
// Execute third query with parameters from second query and store results in variables
ref_by_coin_coin_pub = ref_coin_pub;
ref_by_coin_deposit_serial_id = ref_deposit_serial_id;
// LOOP TO GET THE SUM FROM REFUND BY COIN
for (unsigned int i = 0; i<SPI_processed; i++)
{
if ((ref_by_coin_coin_pub ==
DatumGetByteaP(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &is_null)))
&&
(ref_by_coin_deposit_serial_id ==
DatumGetUInt64(SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &is_null)))
)
{
sum_refund_val += ref_refund_val;
sum_refund_frac += ref_refund_frac;
norm_ref_by_coin_coin_pub = ref_by_coin_coin_pub;
norm_ref_by_coin_deposit_serial_id = ref_by_coin_deposit_serial_id;
}
}// END SUM CALCULATION
//NORMALIZE REFUND VAL FRAC
norm_refund_val =
(sum_refund_val + sum_refund_frac ) / 100000000;
norm_refund_frac =
sum_refund_frac % 100000000;
// Get refund values
s_refund_val += sum_refund_val;
s_refund_frac = sum_refund_frac;
}//END REFUND
if (norm_ref_by_coin_coin_pub == dep_coin_pub
&& ref_by_coin_deposit_serial_id == dep_deposit_serial_ids
&& norm_refund_val == dep_amount_val
&& norm_refund_frac == dep_amount_frac)
{
new_dep_coin_pub = dep_coin_pub;
}
// Ensure we get the fee for each coin and not only once per denomination
if (fees_plan == NULL )
{
const char * fees_sql =
"SELECT "
" denom.fee_deposit_val AS fee_val, "
" denom.fee_deposit_frac AS fee_frac, "
"FROM known_coins kc"
"JOIN denominations denom USING (denominations_serial) "
"WHERE kc.coin_pub = $1 AND kc.coin_pub != $2;";
SPIPlanPtr new_plan = SPI_prepare(fees_sql, 3, (Oid[]){BYTEAOID, BYTEAOID});
if (new_plan == NULL)
{
elog(ERROR, "SPI_prepare for fees failed ! \n");
}
fees_plan = SPI_saveplan(new_plan);
if (fees_plan == NULL)
{
elog(ERROR, "SPI_saveplan for fees failed ! \n");
}
}
values_fees[0] = PointerGetDatum(dep_coin_pub);
values_fees[1] = PointerGetDatum(new_dep_coin_pub);
res = SPI_execute_plan(fees_plan, values_fees, NULL, false, 0);
if (res != SPI_OK_SELECT)
elog(ERROR, "SPI_execute_plan failed for fees \n");
fees_res = SPI_tuptable;
tupdesc = fees_res->tupdesc;
for (unsigned int i = 0; i<SPI_processed; i++)
{
HeapTuple tuple = fees_res->vals[i];
bool is_null;
uint64_t fee_val = DatumGetUInt64(SPI_getbinval(tuple, tupdesc, 1, &is_null));
uint32_t fee_frac = DatumGetUInt32(SPI_getbinval(tuple, tupdesc, 2, &is_null));
uint64_t fees_deposit_serial_id = DatumGetUInt64(SPI_getbinval(tuple, tupdesc, 3, &is_null));
if (dummy_plan == NULL)
{
const char *insert_dummy_sql =
"INSERT INTO "
"aggregation_tracking(deposit_serial_id, wtid_raw)"
" VALUES ($1, $2)";
SPIPlanPtr new_plan = SPI_prepare(insert_dummy_sql, 2, (Oid[]){INT8OID, BYTEAOID});
if (new_plan == NULL)
elog(ERROR, "FAILED to prepare aggregation tracking \n");
dummy_plan = SPI_saveplan(new_plan);
if ( dummy_plan == NULL )
elog(ERROR, "FAILED to saveplan aggregation tracking\n");
}
values_dummys[0] = Int64GetDatum(dep_deposit_serial_ids);
values_dummys[1] = PointerGetDatum(wtid_raw);
res = SPI_execute_plan(dummy_plan, values_dummys, NULL, false, 0);
if (res != SPI_OK_INSERT)
elog(ERROR, "Failed to insert dummy\n");
dummys_res = SPI_tuptable;
// Calculation of deposit fees for not fully refunded deposits
sum_dep_fee_val += fee_val;
sum_dep_fee_frac += fee_frac;
}
// Get deposit values
sum_deposit_val += dep_amount_val;
sum_deposit_frac += dep_amount_frac;
}//END DEPOSIT
SPI_finish();
PG_RETURN_VOID();
}

View File

@ -0,0 +1,4 @@
comment = 'Example extension for testing purposes'
default_version = '1.0'
module_pathname = '$libdir/own_test'
relocatable = true

BIN
src/exchangedb/spi/own_test.so Executable file

Binary file not shown.

View File

@ -0,0 +1,216 @@
DROP TABLE joseph_test.X;
CREATE TABLE joseph_test.X (
a integer
);
INSERT INTO joseph_test.X (a) VALUES (1), (2), (3), (4), (5), (6), (7);
DROP TABLE joseph_test.Y;
CREATE TABLE joseph_test.Y (col1 INT, col2 INT);
INSERT INTO joseph_test.Y (col1,col2) VALUES (1,2), (2,0), (0,4), (4,0), (0,6), (6,7), (7,8);
DROP TABLE joseph_test.Z;
CREATE TABLE joseph_test.Z(col1 BYTEA);
DROP TABLE deposits;
/*CREATE TABLE deposits(
deposit_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
,shard INT8 NOT NULL
,coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)
,known_coin_id INT8 NOT NULL
,amount_with_fee_val INT8 NOT NULL
,amount_with_fee_frac INT4 NOT NULL
,wallet_timestamp INT8 NOT NULL
,exchange_timestamp INT8 NOT NULL
,refund_deadline INT8 NOT NULL
,wire_deadline INT8 NOT NULL
,merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)
,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
,coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)
,wire_salt BYTEA NOT NULL CHECK (LENGTH(wire_salt)=16)
,wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)
,done BOOLEAN NOT NULL DEFAULT FALSE
,policy_blocked BOOLEAN NOT NULL DEFAULT FALSE
,policy_details_serial_id INT8);
*/
--INSERT INTO deposits VALUES ();
CREATE TABLE deposits(
deposit_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
,shard INT8 NOT NULL
,coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)
,known_coin_id INT8 NOT NULL
,amount_with_fee_val INT8 NOT NULL
,amount_with_fee_frac INT4 NOT NULL
,wallet_timestamp INT8 NOT NULL
,exchange_timestamp INT8 NOT NULL
,refund_deadline INT8 NOT NULL
,wire_deadline INT8 NOT NULL
,merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)
,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
,coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)
,wire_salt BYTEA NOT NULL CHECK (LENGTH(wire_salt)=16)
,wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)
,done BOOLEAN NOT NULL DEFAULT FALSE
,policy_blocked BOOLEAN NOT NULL DEFAULT FALSE
,policy_details_serial_id INT8);
CREATE OR REPLACE FUNCTION pg_spi_insert_int()
RETURNS VOID
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_insert_int';
DROP FUNCTION pg_spi_select_from_x();
CREATE OR REPLACE FUNCTION pg_spi_select_from_x()
RETURNS INT8
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_select_from_x';
/*DROP FUNCTION pg_spi_select_pair_from_y();
CREATE OR REPLACE FUNCTION pg_spi_select_pair_from_y()
RETURNS valuest
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_select_pair_from_y';
*/
/*CREATE OR REPLACE FUNCTION pg_spi_select_with_cond()
RETURNS INT8
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_select_with_cond';
*/
DROP FUNCTION pg_spi_update_y();
CREATE OR REPLACE FUNCTION pg_spi_update_y()
RETURNS VOID
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_update_y';
DROP FUNCTION pg_spi_prepare_example();
CREATE OR REPLACE FUNCTION pg_spi_prepare_example()
RETURNS INT8
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_prepare_example';
DROP FUNCTION pg_spi_prepare_example_without_saveplan();
CREATE OR REPLACE FUNCTION pg_spi_prepare_example_without_saveplan()
RETURNS INT8
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_prepare_example_without_saveplan';
CREATE OR REPLACE FUNCTION pg_spi_prepare_insert()
RETURNS VOID
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_prepare_insert';
CREATE OR REPLACE FUNCTION pg_spi_prepare_insert_without_saveplan()
RETURNS VOID
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_prepare_insert_without_saveplan';
/*DROP FUNCTION pg_spi_prepare_select_with_cond();
CREATE OR REPLACE FUNCTION pg_spi_prepare_select_with_cond()
RETURNS INT8
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_prepare_select_with_cond';
*/
DROP FUNCTION pg_spi_prepare_select_with_cond_without_saveplan();
CREATE OR REPLACE FUNCTION pg_spi_prepare_select_with_cond_without_saveplan()
RETURNS INT8
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_prepare_select_with_cond_without_saveplan';
DROP FUNCTION pg_spi_prepare_update();
CREATE OR REPLACE FUNCTION pg_spi_prepare_update()
RETURNS VOID
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_prepare_update';
DROP FUNCTION pg_spi_get_dep_ref_fees(
IN in_timestamp INT8
,IN merchant_pub BYTEA
,IN wire_target_h_payto BYTEA
,IN wtid BYTEA);
CREATE OR REPLACE FUNCTION pg_spi_get_dep_ref_fees(
IN in_timestamp INT8
,IN merchant_pub BYTEA
,IN wire_target_h_payto BYTEA
,IN wtid BYTEA
)
RETURNS VOID
LANGUAGE c COST 100
AS '$libdir/own_test', 'pg_spi_get_dep_ref_fees';
CREATE OR REPLACE FUNCTION update_pg_spi_get_dep_ref_fees(
IN in_refund_deadline INT8,
IN in_merchant_pub BYTEA,
IN in_wire_target_h_payto BYTEA
)
RETURNS SETOF record
LANGUAGE plpgsql VOLATILE
AS $$
DECLARE
BEGIN
RETURN QUERY
UPDATE deposits
SET done = TRUE
WHERE NOT (done OR policy_blocked)
AND refund_deadline < in_refund_deadline
AND merchant_pub = in_merchant_pub
AND wire_target_h_payto = in_wire_target_h_payto
RETURNING
deposit_serial_id,
coin_pub,
amount_with_fee_val,
amount_with_fee_frac;
END $$;
CREATE OR REPLACE FUNCTION stored_procedure_update(
IN in_number INT8
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE joseph_test.Y
SET col1 = 4
WHERE col2 = in_number;
END $$;
CREATE OR REPLACE FUNCTION stored_procedure_select(OUT out_value INT8)
RETURNS INT8
LANGUAGE plpgsql
AS $$
BEGIN
SELECT 1
INTO out_value
FROM joseph_test.X;
RETURN;
END $$;
CREATE OR REPLACE FUNCTION stored_procedure_insert(
IN in_number INT8,
OUT out_number INT8)
RETURNS INT8
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO joseph_test.X (a)
VALUES (in_number)
RETURNING a INTO out_number;
END $$;
CREATE OR REPLACE FUNCTION stored_procedure_select_with_cond(
IN in_number INT8,
OUT out_number INT8
)
RETURNS INT8
LANGUAGE plpgsql
AS $$
BEGIN
SELECT col1 INTO out_number
FROM joseph_test.Y
WHERE col2 = in_number;
RETURN;
END $$;

View File

@ -0,0 +1,25 @@
/*
This file is part of TALER
Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file exchangedb/spi/perf_own_test.c
* @brief benchmark for 'own_test'
* @author Joseph Xu
*/
#include "exchangedb/platform.h"
#include "exchangedb/taler_exchangedb_lib.h"
#include "exchangedb/taler_json_lib.h"
#include "exchangedb/taler_exchangedb_plugin.h"
#include "own_test.sql"

View File

@ -0,0 +1,389 @@
#include "postgres.h"
#include "fmgr.h"
#include "utils/numeric.h"
#include "utils/builtins.h"
#include "executor/spi.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(get_deposit_summary);
Datum get_deposit_summary(PG_FUNCTION_ARGS)
{
static SPIPlanPtr deposit_plan;
static SPIPlanPtr refund_plan;
static SPIPlanPtr refund_by_coin_plan;
static SPIPlanPtr norm_refund_by_coin_plan;
static SPIPlanPtr fully_refunded_by_coins_plan;
static SPIPlanPtr fees_plan;
int shard = PG_GETARG_INT32(0);
char * sql;
char *merchant_pub = text_to_cstring(PG_GETARG_TEXT_P(1));
char *wire_target_h_payto = text_to_cstring(PG_GETARG_TEXT_P(2));
char *wtid_raw = text_to_cstring(PG_GETARG_TEXT_P(3));
int refund_deadline = PG_GETARG_INT32(4);
int conn = SPI_connect();
if (conn != SPI_OK_CONNECT)
{
elog(ERROR, "DB connexion failed ! \n");
}
if ( deposit_plan == NULL
|| refund_plan == NULL
|| refund_by_coin_plan == NULL
|| norm_refund_by_coin_plan = NULL
|| fully_refunded_coins_plan = NULL
|| fees_plan == NULL )
{
if (deposit_plan == NULL)
{
int nargs = 3;
Oid argtypes[3];
argtypes[0] = INT8OID;
argtypes[1] = BYTEAOID;
argtypes[2] = BYTEAOID;
const char *dep_sql =
" UPDATE deposits"
" SET done=TRUE"
" WHERE NOT (done OR policy_blocked)"
" AND refund_deadline < $1"
" AND merchant_pub = $2"
" AND wire_target_h_payto = $3"
" RETURNING"
" deposit_serial_id"
" ,coin_pub"
" ,amount_with_fee_val AS amount_val"
" ,amount_with_fee_frac AS amount_frac";
SPIPlanPtr new_plan =
SPI_prepare(dep_sql, 4, argtypes});
if (new_plan == NULL)
{
elog(ERROR, "SPI_prepare for deposit failed ! \n");
}
deposit_plan = SPI_saveplan(new_plan);
if (deposit_plan == NULL)
{
elog(ERROR, "SPI_saveplan for deposit failed ! \n");
}
}
Datum values[4];
values[0] = Int64GetDatum(refund_deadline);
values[1] = CStringGetDatum(merchant_pub);
values[2] = CStringGetDatum(wire_target_h_payto);
int ret = SPI_execute_plan (deposit_plan,
values,
NULL,
true,
0);
if (ret != SPI_OK_UPDATE)
{
elog(ERROR, "Failed to execute subquery 1\n");
}
uint64_t *dep_deposit_serial_ids = palloc(sizeof(uint64_t) * SPI_processed);
BYTEA **dep_coin_pubs = palloc(sizeof(BYTEA *) * SPI_processed);
uint64_t *dep_amount_vals = palloc(sizeof(uint64_t) * SPI_processed);
uint32_t *dep_amount_fracs = palloc(sizeof(uint32_t) * SPI_processed);
for (unsigned int i = 0; i < SPI_processed; i++) {
HeapTuple tuple = SPI_tuptable->vals[i];
dep_deposit_serial_ids[i] =
DatumGetInt64(SPI_getbinval(tuple, SPI_tuptable->tupdesc, 1, &ret));
dep_coin_pubs[i] =
DatumGetByteaP(SPI_getbinval(tuple, SPI_tuptable->tupdesc, 2, &ret));
dep_amount_vals[i] =
DatumGetInt64(SPI_getbinval(tuple, SPI_tuptable->tupdesc, 3, &ret));
dep_amount_fracs[i] =
DatumGetInt32(SPI_getbinval(tuple, SPI_tuptable->tupdesc, 4, &ret));
}
if (refund_plan == NULL)
{
const char *ref_sql =
"ref AS ("
" SELECT"
" amount_with_fee_val AS refund_val"
" ,amount_with_fee_frac AS refund_frac"
" ,coin_pub"
" ,deposit_serial_id"
" FROM refunds"
" WHERE coin_pub IN (SELECT coin_pub FROM dep)"
" AND deposit_serial_id IN (SELECT deposit_serial_id FROM dep)) ";
SPIPlanPtr new_plan = SPI_prepare(ref_sql, 0, NULL);
if (new_plan == NULL)
elog (ERROR, "SPI_prepare for refund failed ! \n");
refund_plan = SPI_saveplan(new_plan);
if (refund_plan == NULL)
{
elog(ERROR, "SPI_saveplan for refund failed ! \n");
}
}
int64t_t *ref_deposit_serial_ids = palloc(sizeof(int64_t) * SPI_processed);
int res = SPI_execute_plan (refund_plan, NULL, NULL, false, 0);
if (res != SPI_OK_SELECT)
{
elog(ERROR, "Failed to execute subquery 2\n");
}
SPITupleTable *tuptable = SPI_tuptable;
TupleDesc tupdesc = tuptable->tupdesc;
for (unsigned int i = 0; i < SPI_processed; i++)
{
HeapTuple tuple = tuptable->vals[i];
Datum refund_val = SPI_getbinval(tuple, tupdesc, 1, &refund_val_isnull);
Datum refund_frac = SPI_getbinval(tuple, tupdesc, 2, &refund_frac_isnull);
Datum coin_pub = SPI_getbinval(tuple, tupdesc, 3, &coin_pub_isnull);
Datum deposit_serial_id = SPI_getbinval(tuple, tupdesc, 4, &deposit_serial_id_isnull);
if (refund_val_isnull
|| refund_frac_isnull
|| coin_pub_isnull
|| deposit_serial_id_isnull )
{
elog(ERROR, "Failed to retrieve data from subquery 2");
}
uint64_t refund_val_int = DatumGetUInt64(refund_val);
uint32_t refund_frac_int = DatumGetUInt32(refund_frac);
BYTEA coin_pub = DatumGetByteaP(coin_pub);
ref_deposit_serial_ids = DatumGetInt64(deposit_serial_id);
refund *new_refund = (refund*) palloc(sizeof(refund));
new_refund->coin_pub = coin_pub_str;
new_refund->deposit_serial_id = deposit_serial_id_int;
new_refund->amount_with_fee_val = refund_val_int;
new_refund->amount_with_fee_frac = refund_frac_int;
}
if (refund_by_coin_plan == NULL)
{
const char *ref_by_coin_sql =
"ref_by_coin AS ("
" SELECT"
" SUM(refund_val) AS sum_refund_val"
" ,SUM(refund_frac) AS sum_refund_frac"
" ,coin_pub"
" ,deposit_serial_id"
" FROM ref"
" GROUP BY coin_pub, deposit_serial_id) ";
SPIPlanPtr new_plan = SPI_prepare (ref_by_coin_sql, 0, NULL);
if (new_plan == NULL)
elog(ERROR, "SPI_prepare for refund by coin failed ! \n");
refund_by_coin_plan = SPI_saveplan (new_plan);
if (refund_by_coin_plan == NULL)
elog(ERROR, "SPI_saveplan for refund failed");
}
int res = SPI_execute_plan (refund_by_coin_plan, NULL, NULL, false, 0);
if (res != SPI_OK_SELECT)
{
elog(ERROR, "Failed to execute subquery 2\n");
}
SPITupleTable *tuptable = SPI_tuptable;
TupleDesc tupdesc = tuptable->tupdesc;
for (unsigned int i = 0; i < SPI_processed; i++)
{
HeapTuple tuple = tuptable->vals[i];
Datum sum_refund_val = SPI_getbinval(tuple, tupdesc, 1, &refund_val_isnull);
Datum sum_refund_frac = SPI_getbinval(tuple, tupdesc, 2, &refund_frac_isnull);
Datum coin_pub = SPI_getbinval(tuple, tupdesc, 3, &coin_pub_isnull);
Datum deposit_serial_id_int = SPI_getbinval(tuple, tupdesc, 4, &deposit_serial_id_isnull);
if (refund_val_isnull
|| refund_frac_isnull
|| coin_pub_isnull
|| deposit_serial_id_isnull )
{
elog(ERROR, "Failed to retrieve data from subquery 2");
}
uint64_t s_refund_val_int = DatumGetUInt64(sum_refund_val);
uint32_t s_refund_frac_int = DatumGetUInt32(sum_refund_frac);
BYTEA coin_pub = DatumGetByteaP(coin_pub);
uint64_t deposit_serial_id_int = DatumGetInt64(deposit_serial_id_int);
refund *new_refund_by_coin = (refund*) palloc(sizeof(refund));
new_refund_by_coin->coin_pub = coin_pub;
new_refund_by_coin->deposit_serial_id = deposit_serial_id_int;
new_refund_by_coin->refund_amount_with_fee_val = s_refund_val_int;
new_refund_by_coin->refund_amount_with_fee_frac = s_refund_frac_int;
}
if (norm_refund_by_coin_plan == NULL)
{
const char *norm_ref_by_coin_sql =
"norm_ref_by_coin AS ("
" SELECT"
" coin_pub"
" ,deposit_serial_id"
" FROM ref_by_coin) ";
SPIPlanPtr new_plan = SPI_prepare (norm_ref_by_coin_sql, 0, NULL);
if (new_plan == NULL)
elog(ERROR, "SPI_prepare for norm refund by coin failed ! \n");
norm_refund_by_coin_plan = SPI_saveplan(new_plan);
if (norm_refund_by_coin_plan == NULL)
elog(ERROR, "SPI_saveplan for norm refund by coin failed ! \n");
}
double norm_refund_val =
((double)new_refund_by_coin->refund_amount_with_fee_val
+ (double)new_refund_by_coin->refund_amount_with_fee_frac) / 100000000;
double norm_refund_frac =
(double)new_refund_by_coin->refund_amount_with_fee_frac % 100000000;
if (fully_refunded_coins_plan == NULL)
{
const char *fully_refunded_coins_sql =
"fully_refunded_coins AS ("
" SELECT"
" dep.coin_pub"
" FROM norm_ref_by_coin norm"
" JOIN dep"
" ON (norm.coin_pub = dep.coin_pub"
" AND norm.deposit_serial_id = dep.deposit_serial_id"
" AND norm.norm_refund_val = dep.amount_val"
" AND norm.norm_refund_frac = dep.amount_frac)) ";
SPIPlanPtr new_plan =
SPI_prepare(fully_refunded_coins_sql, 0, NULL);
if (new_plan == NULL)
elog (ERROR, "SPI_prepare for fully refunded coins failed ! \n");
fully_refunded_coins_plan = SPI_saveplan(new_plan);
if (fully_refunded_coins_plan == NULL)
elog (ERROR, "SPI_saveplan for fully refunded coins failed ! \n");
}
int res = SPI_execute_plan(fully_refunded_coins_sql);
if ( res != SPI_OK_SELECT)
elog(ERROR, "Failed to execute subquery 4\n");
SPITupleTable * tuptable = SPI_tuptable;
TupleDesc tupdesc = tuptable->tupdesc;
BYTEA coin_pub = SPI_getbinval(tuple, tupdesc, 1, &coin_pub_isnull);
if (fees_plan == NULL)
{
const char *fees_sql =
"SELECT "
" denom.fee_deposit_val AS fee_val, "
" denom.fee_deposit_frac AS fee_frac, "
" cs.deposit_serial_id "
"FROM dep cs "
"JOIN known_coins kc USING (coin_pub) "
"JOIN denominations denom USING (denominations_serial) "
"WHERE coin_pub NOT IN (SELECT coin_pub FROM fully_refunded_coins)";
SPIPlanPtr new_plan =
SPI_prepare(fees_sql, 0, NULL);
if (new_plan == NULL)
{
elog(ERROR, "SPI_prepare for fees failed ! \n");
}
fees_plan = SPI_saveplan(new_plan);
if (fees_plan == NULL)
{
elog(ERROR, "SPI_saveplan for fees failed ! \n");
}
}
}
int fees_ntuples;
SPI_execute(fees_sql, true, 0);
if (SPI_result_code() != SPI_OK_SELECT)
{
ereport(
ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("deposit fee query failed: error code %d \n", SPI_result_code())));
}
fees_ntuples = SPI_processed;
if (fees_ntuples > 0)
{
for (i = 0; i < fees_ntuples; i++)
{
Datum fee_val_datum =
SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &fee_null);
Datum fee_frac_datum =
SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &fee_null);
Datum deposit_id_datum =
SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 3, &deposit_null);
if (!fee_null && !deposit_null)
{
int64 fee_val = DatumGetInt64(fee_val_datum);
int32 fee_frac = DatumGetInt32(fee_frac_datum);
int64 deposit_id = DatumGetInt64(deposit_id_datum);
sum_fee_value += fee_val;
sum_fee_fraction += fee_frac;
char *insert_agg_sql =
psprintf(
"INSERT INTO "
"aggregation_tracking(deposit_serial_id, wtid_raw)"
" VALUES (%lld, '%s')",
deposit_id, wtid_raw);
SPI_execute(insert_agg_sql, false, 0);
}
}
}
TupleDesc tupdesc;
SPITupleTable *tuptable = SPI_tuptable;
HeapTuple tuple;
Datum result;
if (tuptable == NULL || SPI_processed != 1)
{
ereport(
ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("Unexpected result \n")));
}
tupdesc = SPI_tuptable->tupdesc;
tuple = SPI_tuptable->vals[0];
result = HeapTupleGetDatum(tuple);
TupleDesc result_desc = CreateTemplateTupleDesc(6, false);
TupleDescInitEntry(result_desc, (AttrNumber) 1, "sum_deposit_value", INT8OID, -1, 0);
TupleDescInitEntry(result_desc, (AttrNumber) 2, "sum_deposit_fraction", INT4OID, -1, 0);
TupleDescInitEntry(result_desc, (AttrNumber) 3, "sum_refund_value", INT8OID, -1, 0);
TupleDescInitEntry(result_desc, (AttrNumber) 4, "sum_refund_fraction", INT4OID, -1, 0);
TupleDescInitEntry(result_desc, (AttrNumber) 5, "sum_fee_value", INT8OID, -1, 0);
TupleDescInitEntry(result_desc, (AttrNumber) 6, "sum_fee_fraction", INT4OID, -1, 0);
int ret = SPI_prepare(sql, 4, argtypes);
if (ret != SPI_OK_PREPARE)
{
elog(ERROR, "Failed to prepare statement: %s \n", sql);
}
ret = SPI_execute_plan(plan, args, nulls, true, 0);
if (ret != SPI_OK_SELECT)
{
elog(ERROR, "Failed to execute statement: %s \n", sql);
}
if (SPI_processed > 0)
{
HeapTuple tuple;
Datum values[6];
bool nulls[6] = {false};
values[0] =
SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &nulls[0]);
values[1] =
SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2, &nulls[1]);
values[2] =
SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 3, &nulls[2]);
values[3] =
SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 4, &nulls[3]);
values[4] =
SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 5, &nulls[4]);
values[5] =
SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 6, &nulls[5]);
tuple = heap_form_tuple(result_desc, values, nulls);
PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
}
SPI_finish();
PG_RETURN_NULL();
}