From 43251ce3a13b16f5d8fa81179f544dc5ac1dcca2 Mon Sep 17 00:00:00 2001 From: Neil Carvalho Date: Fri, 17 Mar 2023 14:28:21 -0300 Subject: [PATCH] Improve error message when query is canceled during streaming When streaming a result in single row mode, if this query is aborted - either because it timed out or some other reason -, `ruby-pg` raises the error: ``` PG::InvalidChangeOfResultFields: number of fields changed in single row mode from X to 0 - this is a sign for intersection with another query ``` This error message is misleading and might confuse people. This commit adds a different error message, using the same exception class, explaining that the query was canceled or timed out. --- ext/pg_result.c | 3 +++ spec/pg/result_spec.rb | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ext/pg_result.c b/ext/pg_result.c index 27ba6bafa..e126fae1b 100644 --- a/ext/pg_result.c +++ b/ext/pg_result.c @@ -1480,6 +1480,9 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da if( pgresult == NULL ) rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query"); + if( nfields != PQnfields(pgresult) && PQnfields(pgresult) == 0 ) + rb_raise(rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode to 0 - this is a sign that the query was canceled or timed out"); + if( nfields != PQnfields(pgresult) ) rb_raise( rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode from %d to %d - this is a sign for intersection with another query", nfields, PQnfields(pgresult)); diff --git a/spec/pg/result_spec.rb b/spec/pg/result_spec.rb index 613c60ad7..2f2dec2cd 100644 --- a/spec/pg/result_spec.rb +++ b/spec/pg/result_spec.rb @@ -241,7 +241,7 @@ }.to raise_error(PG::DivisionByZero) end - it "raises an error if result number of rows change" do + it "raises an error if result number of fields change" do @conn.send_query( "SELECT 1" ) @conn.set_single_row_mode expect{ @@ -251,6 +251,18 @@ end }.to raise_error(PG::InvalidChangeOfResultFields, /from 1 to 2 /) end + + it "raises an error if there is a timeout during streaming" do + @conn.exec( "SET local statement_timeout = 20" ) + + @conn.send_query( "SELECT 1, true UNION ALL SELECT 2, (pg_sleep(0.1) IS NULL)" ) + @conn.set_single_row_mode + expect{ + @conn.get_result.stream_each_row do |row| + # No-op + end + }.to raise_error(PG::InvalidChangeOfResultFields, /canceled or timed out/) + end end it "inserts nil AS NULL and return NULL as nil" do