mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-03 18:57:06 +01:00
plugins/sql: allow some simple functions.
And document them! Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
14aac0769c
commit
9a591277f5
3 changed files with 84 additions and 3 deletions
|
@ -53,6 +53,28 @@ represented as an integer in the database, so a query will return 0 or
|
||||||
* JSON: string
|
* JSON: string
|
||||||
* sqlite3: TEXT
|
* sqlite3: TEXT
|
||||||
|
|
||||||
|
PERMITTED SQLITE3 FUNCTIONS
|
||||||
|
---------------------------
|
||||||
|
Writing to the database is not permitted, and limits are placed
|
||||||
|
on various other query parameters.
|
||||||
|
|
||||||
|
Additionally, only the following functions are allowed:
|
||||||
|
|
||||||
|
* abs
|
||||||
|
* avg
|
||||||
|
* coalesce
|
||||||
|
* count
|
||||||
|
* hex
|
||||||
|
* quote
|
||||||
|
* length
|
||||||
|
* like
|
||||||
|
* lower
|
||||||
|
* upper
|
||||||
|
* min
|
||||||
|
* max
|
||||||
|
* sum
|
||||||
|
* total
|
||||||
|
|
||||||
TABLES
|
TABLES
|
||||||
------
|
------
|
||||||
[comment]: # (GENERATE-DOC-START)
|
[comment]: # (GENERATE-DOC-START)
|
||||||
|
|
|
@ -264,6 +264,38 @@ static int sqlite_authorize(void *dbq_, int code,
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Some functions are fairly necessary: */
|
||||||
|
if (code == SQLITE_FUNCTION) {
|
||||||
|
if (streq(b, "abs"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "avg"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "coalesce"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "count"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "hex"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "quote"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "length"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "like"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "lower"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "upper"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "min"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "max"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "sum"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
if (streq(b, "total"))
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/* See https://www.sqlite.org/c3ref/c_alter_table.html to decode these! */
|
/* See https://www.sqlite.org/c3ref/c_alter_table.html to decode these! */
|
||||||
dbq->authfail = tal_fmt(dbq, "Unauthorized: %u arg1=%s arg2=%s dbname=%s caller=%s",
|
dbq->authfail = tal_fmt(dbq, "Unauthorized: %u arg1=%s arg2=%s dbname=%s caller=%s",
|
||||||
code,
|
code,
|
||||||
|
|
|
@ -3280,10 +3280,16 @@ def test_block_added_notifications(node_factory, bitcoind):
|
||||||
@pytest.mark.openchannel('v2')
|
@pytest.mark.openchannel('v2')
|
||||||
@pytest.mark.developer("wants dev-announce-localhost so we see listnodes.addresses")
|
@pytest.mark.developer("wants dev-announce-localhost so we see listnodes.addresses")
|
||||||
def test_sql(node_factory, bitcoind):
|
def test_sql(node_factory, bitcoind):
|
||||||
|
opts = {'experimental-offers': None,
|
||||||
|
'dev-allow-localhost': None}
|
||||||
|
l2opts = {'lease-fee-basis': 50,
|
||||||
|
'lease-fee-base-sat': '2000msat',
|
||||||
|
'channel-fee-max-base-msat': '500sat',
|
||||||
|
'channel-fee-max-proportional-thousandths': 200,
|
||||||
|
'sqlfilename': 'sql.sqlite3'}
|
||||||
|
l2opts.update(opts)
|
||||||
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True,
|
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True,
|
||||||
opts={'experimental-offers': None,
|
opts=[opts, l2opts, opts])
|
||||||
'sqlfilename': 'sql.sqlite3',
|
|
||||||
'dev-allow-localhost': None})
|
|
||||||
|
|
||||||
ret = l2.rpc.sql("SELECT * FROM forwards;")
|
ret = l2.rpc.sql("SELECT * FROM forwards;")
|
||||||
assert ret == {'rows': []}
|
assert ret == {'rows': []}
|
||||||
|
@ -3794,6 +3800,27 @@ def test_sql(node_factory, bitcoind):
|
||||||
with pytest.raises(RpcError, match='query failed with no such table: peers_channels'):
|
with pytest.raises(RpcError, match='query failed with no such table: peers_channels'):
|
||||||
l2.rpc.sql("SELECT * FROM peers_channels;")
|
l2.rpc.sql("SELECT * FROM peers_channels;")
|
||||||
|
|
||||||
|
# Test subobject case (option_will_fund)
|
||||||
|
ret = l2.rpc.sql("SELECT option_will_fund_lease_fee_base_msat,"
|
||||||
|
" option_will_fund_lease_fee_basis,"
|
||||||
|
" option_will_fund_funding_weight,"
|
||||||
|
" option_will_fund_channel_fee_max_base_msat,"
|
||||||
|
" option_will_fund_channel_fee_max_proportional_thousandths,"
|
||||||
|
" option_will_fund_compact_lease"
|
||||||
|
" FROM nodes WHERE HEX(nodeid) = '{}';".format(l2.info['id'].upper()))
|
||||||
|
optret = only_one(l2.rpc.listnodes(l2.info['id'])['nodes'])['option_will_fund']
|
||||||
|
row = only_one(ret['rows'])
|
||||||
|
assert row == [v for v in optret.values()]
|
||||||
|
|
||||||
|
# Correctly handles missing object.
|
||||||
|
assert l2.rpc.sql("SELECT option_will_fund_lease_fee_base_msat,"
|
||||||
|
" option_will_fund_lease_fee_basis,"
|
||||||
|
" option_will_fund_funding_weight,"
|
||||||
|
" option_will_fund_channel_fee_max_base_msat,"
|
||||||
|
" option_will_fund_channel_fee_max_proportional_thousandths,"
|
||||||
|
" option_will_fund_compact_lease"
|
||||||
|
" FROM nodes WHERE HEX(nodeid) = '{}';".format(l1.info['id'].upper())) == {'rows': [[None] * 6]}
|
||||||
|
|
||||||
|
|
||||||
def test_sql_deprecated(node_factory, bitcoind):
|
def test_sql_deprecated(node_factory, bitcoind):
|
||||||
# deprecated-apis breaks schemas...
|
# deprecated-apis breaks schemas...
|
||||||
|
|
Loading…
Add table
Reference in a new issue