Prevent duplicate call to stored procs
Reuse a CTE for results of call
This commit is contained in:
@@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- Prevent duplicate call to stored procs (regression) - @begriffs
|
||||||
- Allow SQL functions to generate registered JWT claims - @begriffs
|
- Allow SQL functions to generate registered JWT claims - @begriffs
|
||||||
- Terminate gracefully on SIGTERM (for use in Docker) - @recmo
|
- Terminate gracefully on SIGTERM (for use in Docker) - @recmo
|
||||||
- Relation detection fix for views that depend on multiple tables - @ruslantalpa
|
- Relation detection fix for views that depend on multiple tables - @ruslantalpa
|
||||||
|
|||||||
@@ -208,18 +208,22 @@ callProc :: QualifiedIdentifier -> JSON.Object -> NonnegRange -> Bool -> H.Query
|
|||||||
callProc qi params range countTotal =
|
callProc qi params range countTotal =
|
||||||
H.statement sql HE.unit decodeProc True
|
H.statement sql HE.unit decodeProc True
|
||||||
where
|
where
|
||||||
sql = [qc| SELECT
|
sql = [qc|
|
||||||
{countQuery} as countTotal,
|
WITH t AS (select * {_callSql})
|
||||||
{countResult} as countResult,
|
SELECT
|
||||||
|
{_countExpr} as countTotal,
|
||||||
|
pg_catalog.count(1) as countResult,
|
||||||
array_to_json(
|
array_to_json(
|
||||||
coalesce(array_agg(row_to_json(t)), '\{}')
|
coalesce(array_agg(row_to_json(r)), '\{}')
|
||||||
)::character varying
|
)::character varying
|
||||||
from (select * {_callSql} {limitF range}) t |]
|
FROM (select * from t {limitF range}) r;
|
||||||
|
|]
|
||||||
_args = intercalate "," $ map _assignment (HM.toList params)
|
_args = intercalate "," $ map _assignment (HM.toList params)
|
||||||
_assignment (n,v) = pgFmtIdent n <> ":=" <> insertableValue v
|
_assignment (n,v) = pgFmtIdent n <> ":=" <> insertableValue v
|
||||||
_callSql = [qc| from {fromQi qi}({_args}) |] :: BS.ByteString
|
_callSql = [qc| from {fromQi qi}({_args}) |] :: BS.ByteString
|
||||||
countQuery = if countTotal then [qc| (select pg_catalog.count(1) {_callSql} c) |] else "null::bigint" :: BS.ByteString
|
_countExpr = if countTotal
|
||||||
countResult = "pg_catalog.count(t)" :: BS.ByteString
|
then "(select pg_catalog.count(1) from t)"
|
||||||
|
else "null::bigint" :: BS.ByteString
|
||||||
decodeProc = HD.maybeRow procRow
|
decodeProc = HD.maybeRow procRow
|
||||||
procRow = (,,) <$> HD.nullableValue HD.int8 <*> HD.value HD.int8
|
procRow = (,,) <$> HD.nullableValue HD.int8 <*> HD.value HD.int8
|
||||||
<*> HD.value HD.json
|
<*> HD.value HD.json
|
||||||
|
|||||||
@@ -422,6 +422,12 @@ spec = do
|
|||||||
it "GET with 405 on known procs" $
|
it "GET with 405 on known procs" $
|
||||||
get "/rpc/sayhello" `shouldRespondWith` 405
|
get "/rpc/sayhello" `shouldRespondWith` 405
|
||||||
|
|
||||||
|
it "executes the proc exactly once per request" $ do
|
||||||
|
post "/rpc/callcounter" [json| {} |] `shouldRespondWith`
|
||||||
|
[json| [{"callcounter":1}] |]
|
||||||
|
post "/rpc/callcounter" [json| {} |] `shouldRespondWith`
|
||||||
|
[json| [{"callcounter":2}] |]
|
||||||
|
|
||||||
describe "weird requests" $ do
|
describe "weird requests" $ do
|
||||||
it "can query as normal" $ do
|
it "can query as normal" $ do
|
||||||
get "/Escap3e;" `shouldRespondWith`
|
get "/Escap3e;" `shouldRespondWith`
|
||||||
|
|||||||
Vendored
+1
@@ -43,6 +43,7 @@ GRANT INSERT ON TABLE insertonly TO postgrest_test_anonymous;
|
|||||||
GRANT USAGE ON SEQUENCE
|
GRANT USAGE ON SEQUENCE
|
||||||
auto_incrementing_pk_id_seq
|
auto_incrementing_pk_id_seq
|
||||||
, items_id_seq
|
, items_id_seq
|
||||||
|
, callcounter_count
|
||||||
TO postgrest_test_anonymous;
|
TO postgrest_test_anonymous;
|
||||||
|
|
||||||
-- Privileges for non anonymous users
|
-- Privileges for non anonymous users
|
||||||
|
|||||||
Vendored
+12
@@ -261,6 +261,18 @@ CREATE FUNCTION sayhello(name text) RETURNS text
|
|||||||
$_$;
|
$_$;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: callcounter(); Type: FUNCTION; Schema: test; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE callcounter_count START 1;
|
||||||
|
|
||||||
|
CREATE FUNCTION callcounter() RETURNS bigint
|
||||||
|
LANGUAGE sql
|
||||||
|
AS $_$
|
||||||
|
SELECT nextval('test.callcounter_count');
|
||||||
|
$_$;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: test_empty_rowset(); Type: FUNCTION; Schema: test; Owner: -
|
-- Name: test_empty_rowset(); Type: FUNCTION; Schema: test; Owner: -
|
||||||
--
|
--
|
||||||
|
|||||||
Reference in New Issue
Block a user