-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unable to type check procedure CALL in postgresql #1449
Comments
Do you mind giving the output of A minimal reproduction would be fine (preferable, actually) if you don't want to give the real query plan. |
Hmm, trying to reproduce it myself, you apparently can't do an Although what the macro is doing it's more like: PREPARE call_foo AS CALL foo();
EXPLAIN (VERBOSE, FORMAT JSON) EXECUTE call_foo; But even then you can't use I suspect, however, that the output of Does |
thanks @abonander for answering so quickly! This is what I got from the terminal for the query plan
The procedure has more arguments but pg seems to complain about the syntax anyway The procedure is this one: CREATE OR REPLACE PROCEDURE append_to_store(
notify_aggregate_type_id TEXT,
aggregate_id UUID,
current_version INTEGER,
perform_version_check BOOLEAN,
names TEXT[],
timestamps TIMESTAMPTZ[],
events JSONB[],
INOUT aggregate_version INTEGER DEFAULT NULL,
INOUT sequence_number BIGINT DEFAULT NULL
)
LANGUAGE PLPGSQL
AS $$
DECLARE
"event" JSONB;
event_name TEXT;
timestamp TIMESTAMPTZ;
BEGIN
-- Retrieve the global offset value from the aggregate_type_id.
PERFORM FROM aggregate_types WHERE id = notify_aggregate_type_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'invalid aggregate type provided: %', notify_aggregate_type_id;
END IF;
-- Retrieve the latest aggregate version for the specified aggregate id.
SELECT a."version" INTO aggregate_version FROM aggregates a WHERE id = aggregate_id AND a.aggregate_type_id = notify_aggregate_type_id;
IF NOT FOUND THEN
-- Add the initial aggregate representation inside the `aggregates` table.
INSERT INTO aggregates (id, aggregate_type_id)
VALUES (aggregate_id, notify_aggregate_type_id);
-- Make sure to initialize the aggregate version in case
aggregate_version = 0;
END IF;
-- Perform optimistic concurrency check.
IF perform_version_check AND aggregate_version <> current_version THEN
RAISE EXCEPTION 'invalid aggregate version provided: %, expected: %', current_version, aggregate_version;
END IF;
SELECT events.sequence_number INTO sequence_number from events ORDER BY time DESC LIMIT 1;
FOR i IN 1 .. array_upper(events, 1)
LOOP
event_name = names[i];
"event" = events[i];
timestamp = timestamps[i];
-- Increment the aggregate version prior to inserting the new event.
aggregate_version = aggregate_version + 1;
-- Insert the event into the events table.
-- Version numbers should start from 1; sequence numbers should start from 0.
INSERT INTO events
(time, aggregate_id, aggregate_type_id, "version", name, "event")
VALUES
(coalesce(timestamp, now()), aggregate_id, notify_aggregate_type_id, aggregate_version, event_name, "event")
RETURNING
events.sequence_number INTO sequence_number;
-- Send a notification to all listeners of the newly added events.
PERFORM pg_notify(notify_aggregate_type_id, ''
|| '{'
|| '"source_id": "' || aggregate_id || '" ,'
|| '"version": ' || aggregate_version || ', '
|| '"sequence_number": ' || sequence_number || ', '
|| '"event": ' || "event"::TEXT
|| '}');
COMMIT;
END LOOP;
-- Update the aggregate with the latest version computed.
UPDATE aggregates a SET "version" = aggregate_version WHERE id = aggregate_id AND a.aggregate_type_id = notify_aggregate_type_id;
-- Update the global offset with the latest sequence number.
UPDATE aggregate_types SET "offset" = sequence_number WHERE id = notify_aggregate_type_id;
END;
$$; It's a custom version of this procedure |
Hmm, okay. So there's a couple of orthogonal issues here:
[
"Utility Statement",
// corresponding to the first `SELECT` statement
{
"plan": { ... }
},
...
] I'd need to experiment with this, preferably with a minimal reproduction, to see what it actually returns. If I might make a suggestion though, I would recommend not baking lots of business logic into stored procedures in the database like this. It might seem more efficient but it's going to be much more difficult to maintain and reason about. |
Okay, we already aren't doing And I can now confirm that it does just return |
Totally agree with not putting too much "hidden" logic in procedures. It does feel like it's too much magic in a black box. However, in this case this is part of an event sourcing implementation, and so it relies a lot on integrity checks and the right sequences. I'm actually working right now in simplifying this a bit Postgres 14 will support OUT parameters for procedures, but until then or if older versions support is required, it would have to be INOUT. I'm not sure if I can do anything to help. I'm more than happy to try anything out |
The fix is relatively simple, it just sadly means that the macros are rather limited in their ability to handle procedure calls. |
Fixes launchbadge#1449 (I think). I verified that the code fixes the new test. I used INOUT in setup.sql because older versions of Postgres don't support OUT parameters.
Fixes launchbadge#1449 (I think). I verified that the code fixes the new test. I used INOUT in setup.sql because older versions of Postgres don't support OUT parameters.
I think my PR #2271 will fix this issue. |
Fixes launchbadge#1449 (I think). I verified that the code fixes the new test. I used INOUT in setup.sql because older versions of Postgres don't support OUT parameters.
Fixes #1449 (I think). I verified that the code fixes the new test. I used INOUT in setup.sql because older versions of Postgres don't support OUT parameters.
Fixes launchbadge#1449 (I think). I verified that the code fixes the new test. I used INOUT in setup.sql because older versions of Postgres don't support OUT parameters.
``` error: error occurred while decoding column 0: data did not match any variant of untagged enum Explain at line 3 column 1 Error: --> tests/postgres/macros.rs:103:15 | 103 | let row = sqlx::query!(r#"CALL forty_two(null)"#) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `$crate::sqlx_macros::expand_query` which comes from the expansion of the macro `sqlx::query` (in Nightly builds, run with -Z macro-backtrace for more info) error: could not compile `sqlx` (test "postgres-macros") due to previous error ```
``` error: error occurred while decoding column 0: data did not match any variant of untagged enum Explain at line 3 column 1 Error: --> tests/postgres/macros.rs:103:15 | 103 | let row = sqlx::query!(r#"CALL forty_two(null)"#) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `$crate::sqlx_macros::expand_query` which comes from the expansion of the macro `sqlx::query` (in Nightly builds, run with -Z macro-backtrace for more info) error: could not compile `sqlx` (test "postgres-macros") due to previous error ```
``` error: error occurred while decoding column 0: data did not match any variant of untagged enum Explain at line 3 column 1 Error: --> tests/postgres/macros.rs:103:15 | 103 | let row = sqlx::query!(r#"CALL forty_two(null)"#) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `$crate::sqlx_macros::expand_query` which comes from the expansion of the macro `sqlx::query` (in Nightly builds, run with -Z macro-backtrace for more info) error: could not compile `sqlx` (test "postgres-macros") due to previous error ```
* fix(postgres): sqlx prepare fails if shared_preload_libraries=pg_stat_statements closes #2622 refs: * https://serde.rs/enum-representations.html#untagged * https://serde.rs/field-attrs.html#skip * https://www.postgresql.org/docs/current/pgstatstatements.html * https://www.postgresql.org/docs/current/runtime-config-statistics.html#GUC-COMPUTE-QUERY-ID * fix(postgres): regression of #1449 ``` error: error occurred while decoding column 0: data did not match any variant of untagged enum Explain at line 3 column 1 Error: --> tests/postgres/macros.rs:103:15 | 103 | let row = sqlx::query!(r#"CALL forty_two(null)"#) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `$crate::sqlx_macros::expand_query` which comes from the expansion of the macro `sqlx::query` (in Nightly builds, run with -Z macro-backtrace for more info) error: could not compile `sqlx` (test "postgres-macros") due to previous error ``` * refactor(postgres): don't define unused fields in QueryPlan * refactor(postgres): simplify query plan handling, add unit test * chore: document why we load `pg_stat_statements` in tests --------- Co-authored-by: mrl5 <[email protected]>
I have a simple query like this
CALL my_procedure($1)
and I'm trying to use it with the type-checking macro like:
But I'm getting this error
If I use the query without type checking like:
it works fine, so it seems an issue with the macro and the parsing of procedure calls
The text was updated successfully, but these errors were encountered: