Error invalid transaction termination

I have some procedure which when i execute in dbeaver works fine without issue, however when i call it from outside program i am getting error below. I do not want to copy/paste full procedure here

I have some procedure which when i execute in dbeaver works fine without issue, however when i call it from outside program i am getting error below. I do not want to copy/paste full procedure here because it’s pretty big and it works in db tool. I just copy/paste top and bottom. What could be the cause of it?

Procedure:

CREATE OR REPLACE PROCEDURE MyProcedure(lot of args..)
 LANGUAGE plpgsql
AS $procedure$
DECLARE
.....
.....
COMMIT;
END;
$procedure$
;

Error:

ERROR: invalid transaction termination
  Where: PL/pgSQL function MyFunction line 185 at COMMIT  Call getNextException to see other errors in the batch. Line 185 at COMMIT  Call getNextException to see other errors in the batch.

asked Dec 3, 2019 at 14:15

Henry's user avatar

The documentation says:

Transaction control is only possible in CALL or DO invocations from the top level or nested CALL or DO invocations without any other intervening command. For example, if the call stack is CALL proc1()CALL proc2()CALL proc3(), then the second and third procedures can perform transaction control actions. But if the call stack is CALL proc1()SELECT func2()CALL proc3(), then the last procedure cannot do transaction control, because of the SELECT in between.

There are some other, undocumented, restrictions:

  • You cannot start a transaction explicitly with BEGIN and commit it inside a transaction. So the following will fail:

    START TRANSACTION;
    CALL procedure_with_commit();
    

    This may be improved in future releases.

  • All procedures in the call stack must be written in PL/pgSQL:

    CREATE PROCEDURE b() LANGUAGE plpgsql
       AS 'BEGIN PERFORM 42; COMMIT; END;';
    
    CREATE PROCEDURE a() LANGUAGE sql
       AS 'CALL b()';
    
    CALL a();
    ERROR:  invalid transaction termination
    CONTEXT:  PL/pgSQL function b() line 1 at COMMIT
    SQL function "a" statement 1
    

As it is, transaction control inside PostgreSQL procedures is somewhat limited.

If you violate any of these rules, you will get the error message you describe in your question. You will probably have to handle transactions in the application rather than in the procedure — perhaps splitting the procedure into smaller parts makes this possible.

answered Dec 3, 2019 at 14:37

Laurenz Albe's user avatar

Laurenz AlbeLaurenz Albe

192k17 gold badges177 silver badges233 bronze badges

1

Содержание

  1. Transaction management in PostgreSQL and what is different from Oracle
  2. TL;DR: Everything is different about transaction behavior. This may also change your ideas about “database independent” applications.
  3. Auto commit
  4. Commit on exit
  5. DDL auto-commit
  6. Start transaction
  7. Set Transaction name
  8. Autonomous Transaction
  9. User Call level rollback
  10. ERROR: current transaction is aborted
  11. Savepoint
  12. PostgreSQL 11 Procedures
  13. Invalid transaction termination
  14. Implicit transactions
  15. Postgresql error invalid transaction termination
  16. Submit correction

Transaction management in PostgreSQL and what is different from Oracle

TL;DR: Everything is different about transaction behavior. This may also change your ideas about “database independent” applications.

I like to explain some PostgreSQL concepts from an oracle DBA point of view. There are many things that are different in the two RDBMS and it is important to understand them.

Auto commit

Here is a short example where I create a table, insert one row and rollback:

I never executed any commit but my changes are saved and published. This is auto-commit by default in psql. I think I prefer the Oracle default where the commit is my decision, and only what I’ve committed is visible:

So, is Oracle the best for transaction management?

Well… autocommit default is only one thing. Let’s go further.

First, this is only a client setting. SQL*Plus has it set to off:

But JDBC drivers may have it set to on. So, rather than a nice thing about the database, it is just a nice default of SQL*Plus.

Commit on exit

And wait… are all sqlplus defaults so smart? Let’s come back to my Oracle database where I committed nothing:

Are sqlplus defaults so smart? I didn’t commit the last insert but it was committed by default. Not because of auto-commit but because of exit-commit:

That’s not a nice default. If I quit without saying ‘commit’ I want a rollback. It is highly recommended to set exit-commit off to avoid any unexpected commit. (also recommendedvfor scripts having a WHENEVER SQLERROR EXIT that mentions ROLLBACK because COMMIT is the default).

DDL auto-commit

And that’s not all… The first row (n=1) was never explicitly committed. It was inserted with DDL (CREATE TABLE) and DDL are always auto-committed. That’s the Oracle Database, nothing to do with the client: you cannot be transactional with DDL.

Start transaction

Back to PostgreSQL, the default is auto-commit but I have the choice. I can explicitly start a transaction and then I’ll have to explicitly commit it.

See? I have the 3 rows from the first run which were all auto-committed. But now that I explicitly started a transaction, everything was transactional, even the DDL: the DROP TABLE, the CREATE TABLE, the INSERT were explicitly rolled-back. And even the last INSERT was implicitly rolled-back on exit.

Now, who is the winner in transaction management? There’s even more: you can send a multi-statement command to the backend and it will be processed as an implicit transaction.

Note that START TRANSACTION is the ANSI SQL syntax, but PostgreSQL accepts also BEGIN, BEGIN TRANSACTION and BEGIN WORK.

Set Transaction name

Don’t think that there are no “begin transaction” in Oracle. The SET TRANSACTION starts it, in order to define the isolation level, or simply to put a name to the transaction.

This example looks at the transaction address in V$SESSION and V$TRANSACTION

This illustrates when a transaction starts (visible with a TADDR in V$SESSION and a row in V$TRANSACTION): the first INSERT/DELETE/UPDATE/MERGE/SELECT FOR UPDATE or a SET TRANSACTION

Autonomous Transaction

Not available in PostgreSQL but possible in Oracle: we can have nested transactions. This is very convenient in some limited cases, like logging the error in the database (and commit this insert) before the rollback of the transaction.

This shows that there can be multiple transactions for the same session. The PADDR is only the address or the top-level one.

User Call level rollback

(Note that I’ve previously written “statement-level rollback” but this is misleading because a statement can be executed in a recursive call, as in a PL/SQL block. What I show here is about a user call statement which can actually be a SQL statement or a PL/SQL block or a procedure call ).

Still in Oracle, when a statement fails in a user call, the modifications done by this user call are rolled back, but not the previous modifications. The transaction can continue (like re-try, or do an alternative change):

ERROR: current transaction is aborted

That’s different in PostgreSQL where the transaction cannot continue when you encounter an error:

Here I rolled back. But I can also commit to terminate the transaction, but it will rollback anyway:

Savepoint

Actually, we can achieve user call level rollback even in PostgreSQL, using savepoints:

With Oracle, you don’t need to because there is an implicit savepoint before each execution.

Well, the PostgreSQL client psql do the same:

and then psql will automatically create a “pg_psql_temporary_savepoint”.

PostgreSQL 11 Procedures

Before version 11 all commands (which can be one statement, or multiple ones, or PL/pgSQL anonymous or stored procedures) were run in an atomic context, with no intermediate commits possible without a roundtrip with the client. Like this:

The first call has inserted values 10 and 12 and they were committed because I run in the default AUTOCOMMIT. But the second call has encountered a duplicate key and the whole was rolled-back. But in PostgreSQL 11 I can add an intermediate commit so that the first rows are committed before I encounter the error.

Here the rows 5 to 9 have been committed before encountering the exception for row 10.

Still, no need for an explicit BEGIN here. The COMMIT in the loop will end the transaction (started implicitly on the server as I did no BEGIN before) and start a new one. The last transaction started will be committed implicitly.

Additionally, you can look at the timestamps in the two previous demos. In PostgreSQL, current_timestamp is consistent in the transaction.

Invalid transaction termination

Here is the same, but with AUTOCOMMIT off:

Actually, this is what made me start this blog post. But that’s already a lot, and further investigation on this will be on the next post:
AUTOCOMMIT and PostgreSQL transaction management in procedures

Implicit transactions

Ok, just one more on this. The error above is not really caused by AUTOCOMMIT but by the way psql handles AUTOCOMMIT. Actually, with PostgreSQL, the AUTOCOMMIT is not a BEGIN statement added by the psql client, but the backend server creating an implicit transaction when there is not already one. It is even the opposite: when AUTOCOMMIT is off, the client adds a “BEGIN” so that the server does not create an implicit one. Which means that the “invalid transaction termination” occurs also without auto-commit when we CALL the procedure from an explicit transaction.

This long post is only a short sample of the many differences between Oracle and PostgreSQL transaction management. And I’ve only used psql and sqlplus here. Do you want to test the same from Java JDBC and Python psycopg2?

Источник

Postgresql error invalid transaction termination

All messages emitted by the PostgreSQL server are assigned five-character error codes that follow the SQL standard’s conventions for “ SQLSTATE ” codes. Applications that need to know which error condition has occurred should usually test the error code, rather than looking at the textual error message. The error codes are less likely to change across PostgreSQL releases, and also are not subject to change due to localization of error messages. Note that some, but not all, of the error codes produced by PostgreSQL are defined by the SQL standard; some additional error codes for conditions not defined by the standard have been invented or borrowed from other databases.

According to the standard, the first two characters of an error code denote a class of errors, while the last three characters indicate a specific condition within that class. Thus, an application that does not recognize the specific error code might still be able to infer what to do from the error class.

Table A.1 lists all the error codes defined in PostgreSQL 15.1. (Some are not actually used at present, but are defined by the SQL standard.) The error classes are also shown. For each error class there is a “ standard ” error code having the last three characters 000 . This code is used only for error conditions that fall within the class but do not have any more-specific code assigned.

The symbol shown in the column “ Condition Name ” is the condition name to use in PL/pgSQL . Condition names can be written in either upper or lower case. (Note that PL/pgSQL does not recognize warning, as opposed to error, condition names; those are classes 00, 01, and 02.)

For some types of errors, the server reports the name of a database object (a table, table column, data type, or constraint) associated with the error; for example, the name of the unique constraint that caused a unique_violation error. Such names are supplied in separate fields of the error report message so that applications need not try to extract them from the possibly-localized human-readable text of the message. As of PostgreSQL 9.3, complete coverage for this feature exists only for errors in SQLSTATE class 23 (integrity constraint violation), but this is likely to be expanded in future.

Table A.1. PostgreSQL Error Codes

Error Code Condition Name
Class 00 — Successful Completion
00000 successful_completion
Class 01 — Warning
01000 warning
0100C dynamic_result_sets_returned
01008 implicit_zero_bit_padding
01003 null_value_eliminated_in_set_function
01007 privilege_not_granted
01006 privilege_not_revoked
01004 string_data_right_truncation
01P01 deprecated_feature
Class 02 — No Data (this is also a warning class per the SQL standard)
02000 no_data
02001 no_additional_dynamic_result_sets_returned
Class 03 — SQL Statement Not Yet Complete
03000 sql_statement_not_yet_complete
Class 08 — Connection Exception
08000 connection_exception
08003 connection_does_not_exist
08006 connection_failure
08001 sqlclient_unable_to_establish_sqlconnection
08004 sqlserver_rejected_establishment_of_sqlconnection
08007 transaction_resolution_unknown
08P01 protocol_violation
Class 09 — Triggered Action Exception
09000 triggered_action_exception
Class 0A — Feature Not Supported
0A000 feature_not_supported
Class 0B — Invalid Transaction Initiation
0B000 invalid_transaction_initiation
Class 0F — Locator Exception
0F000 locator_exception
0F001 invalid_locator_specification
Class 0L — Invalid Grantor
0L000 invalid_grantor
0LP01 invalid_grant_operation
Class 0P — Invalid Role Specification
0P000 invalid_role_specification
Class 0Z — Diagnostics Exception
0Z000 diagnostics_exception
0Z002 stacked_diagnostics_accessed_without_active_handler
Class 20 — Case Not Found
20000 case_not_found
Class 21 — Cardinality Violation
21000 cardinality_violation
Class 22 — Data Exception
22000 data_exception
2202E array_subscript_error
22021 character_not_in_repertoire
22008 datetime_field_overflow
22012 division_by_zero
22005 error_in_assignment
2200B escape_character_conflict
22022 indicator_overflow
22015 interval_field_overflow
2201E invalid_argument_for_logarithm
22014 invalid_argument_for_ntile_function
22016 invalid_argument_for_nth_value_function
2201F invalid_argument_for_power_function
2201G invalid_argument_for_width_bucket_function
22018 invalid_character_value_for_cast
22007 invalid_datetime_format
22019 invalid_escape_character
2200D invalid_escape_octet
22025 invalid_escape_sequence
22P06 nonstandard_use_of_escape_character
22010 invalid_indicator_parameter_value
22023 invalid_parameter_value
22013 invalid_preceding_or_following_size
2201B invalid_regular_expression
2201W invalid_row_count_in_limit_clause
2201X invalid_row_count_in_result_offset_clause
2202H invalid_tablesample_argument
2202G invalid_tablesample_repeat
22009 invalid_time_zone_displacement_value
2200C invalid_use_of_escape_character
2200G most_specific_type_mismatch
22004 null_value_not_allowed
22002 null_value_no_indicator_parameter
22003 numeric_value_out_of_range
2200H sequence_generator_limit_exceeded
22026 string_data_length_mismatch
22001 string_data_right_truncation
22011 substring_error
22027 trim_error
22024 unterminated_c_string
2200F zero_length_character_string
22P01 floating_point_exception
22P02 invalid_text_representation
22P03 invalid_binary_representation
22P04 bad_copy_file_format
22P05 untranslatable_character
2200L not_an_xml_document
2200M invalid_xml_document
2200N invalid_xml_content
2200S invalid_xml_comment
2200T invalid_xml_processing_instruction
22030 duplicate_json_object_key_value
22031 invalid_argument_for_sql_json_datetime_function
22032 invalid_json_text
22033 invalid_sql_json_subscript
22034 more_than_one_sql_json_item
22035 no_sql_json_item
22036 non_numeric_sql_json_item
22037 non_unique_keys_in_a_json_object
22038 singleton_sql_json_item_required
22039 sql_json_array_not_found
2203A sql_json_member_not_found
2203B sql_json_number_not_found
2203C sql_json_object_not_found
2203D too_many_json_array_elements
2203E too_many_json_object_members
2203F sql_json_scalar_required
2203G sql_json_item_cannot_be_cast_to_target_type
Class 23 — Integrity Constraint Violation
23000 integrity_constraint_violation
23001 restrict_violation
23502 not_null_violation
23503 foreign_key_violation
23505 unique_violation
23514 check_violation
23P01 exclusion_violation
Class 24 — Invalid Cursor State
24000 invalid_cursor_state
Class 25 — Invalid Transaction State
25000 invalid_transaction_state
25001 active_sql_transaction
25002 branch_transaction_already_active
25008 held_cursor_requires_same_isolation_level
25003 inappropriate_access_mode_for_branch_transaction
25004 inappropriate_isolation_level_for_branch_transaction
25005 no_active_sql_transaction_for_branch_transaction
25006 read_only_sql_transaction
25007 schema_and_data_statement_mixing_not_supported
25P01 no_active_sql_transaction
25P02 in_failed_sql_transaction
25P03 idle_in_transaction_session_timeout
Class 26 — Invalid SQL Statement Name
26000 invalid_sql_statement_name
Class 27 — Triggered Data Change Violation
27000 triggered_data_change_violation
Class 28 — Invalid Authorization Specification
28000 invalid_authorization_specification
28P01 invalid_password
Class 2B — Dependent Privilege Descriptors Still Exist
2B000 dependent_privilege_descriptors_still_exist
2BP01 dependent_objects_still_exist
Class 2D — Invalid Transaction Termination
2D000 invalid_transaction_termination
Class 2F — SQL Routine Exception
2F000 sql_routine_exception
2F005 function_executed_no_return_statement
2F002 modifying_sql_data_not_permitted
2F003 prohibited_sql_statement_attempted
2F004 reading_sql_data_not_permitted
Class 34 — Invalid Cursor Name
34000 invalid_cursor_name
Class 38 — External Routine Exception
38000 external_routine_exception
38001 containing_sql_not_permitted
38002 modifying_sql_data_not_permitted
38003 prohibited_sql_statement_attempted
38004 reading_sql_data_not_permitted
Class 39 — External Routine Invocation Exception
39000 external_routine_invocation_exception
39001 invalid_sqlstate_returned
39004 null_value_not_allowed
39P01 trigger_protocol_violated
39P02 srf_protocol_violated
39P03 event_trigger_protocol_violated
Class 3B — Savepoint Exception
3B000 savepoint_exception
3B001 invalid_savepoint_specification
Class 3D — Invalid Catalog Name
3D000 invalid_catalog_name
Class 3F — Invalid Schema Name
3F000 invalid_schema_name
Class 40 — Transaction Rollback
40000 transaction_rollback
40002 transaction_integrity_constraint_violation
40001 serialization_failure
40003 statement_completion_unknown
40P01 deadlock_detected
Class 42 — Syntax Error or Access Rule Violation
42000 syntax_error_or_access_rule_violation
42601 syntax_error
42501 insufficient_privilege
42846 cannot_coerce
42803 grouping_error
42P20 windowing_error
42P19 invalid_recursion
42830 invalid_foreign_key
42602 invalid_name
42622 name_too_long
42939 reserved_name
42804 datatype_mismatch
42P18 indeterminate_datatype
42P21 collation_mismatch
42P22 indeterminate_collation
42809 wrong_object_type
428C9 generated_always
42703 undefined_column
42883 undefined_function
42P01 undefined_table
42P02 undefined_parameter
42704 undefined_object
42701 duplicate_column
42P03 duplicate_cursor
42P04 duplicate_database
42723 duplicate_function
42P05 duplicate_prepared_statement
42P06 duplicate_schema
42P07 duplicate_table
42712 duplicate_alias
42710 duplicate_object
42702 ambiguous_column
42725 ambiguous_function
42P08 ambiguous_parameter
42P09 ambiguous_alias
42P10 invalid_column_reference
42611 invalid_column_definition
42P11 invalid_cursor_definition
42P12 invalid_database_definition
42P13 invalid_function_definition
42P14 invalid_prepared_statement_definition
42P15 invalid_schema_definition
42P16 invalid_table_definition
42P17 invalid_object_definition
Class 44 — WITH CHECK OPTION Violation
44000 with_check_option_violation
Class 53 — Insufficient Resources
53000 insufficient_resources
53100 disk_full
53200 out_of_memory
53300 too_many_connections
53400 configuration_limit_exceeded
Class 54 — Program Limit Exceeded
54000 program_limit_exceeded
54001 statement_too_complex
54011 too_many_columns
54023 too_many_arguments
Class 55 — Object Not In Prerequisite State
55000 object_not_in_prerequisite_state
55006 object_in_use
55P02 cant_change_runtime_param
55P03 lock_not_available
55P04 unsafe_new_enum_value_usage
Class 57 — Operator Intervention
57000 operator_intervention
57014 query_canceled
57P01 admin_shutdown
57P02 crash_shutdown
57P03 cannot_connect_now
57P04 database_dropped
57P05 idle_session_timeout
Class 58 — System Error (errors external to PostgreSQL itself)
58000 system_error
58030 io_error
58P01 undefined_file
58P02 duplicate_file
Class 72 — Snapshot Failure
72000 snapshot_too_old
Class F0 — Configuration File Error
F0000 config_file_error
F0001 lock_file_exists
Class HV — Foreign Data Wrapper Error (SQL/MED)
HV000 fdw_error
HV005 fdw_column_name_not_found
HV002 fdw_dynamic_parameter_value_needed
HV010 fdw_function_sequence_error
HV021 fdw_inconsistent_descriptor_information
HV024 fdw_invalid_attribute_value
HV007 fdw_invalid_column_name
HV008 fdw_invalid_column_number
HV004 fdw_invalid_data_type
HV006 fdw_invalid_data_type_descriptors
HV091 fdw_invalid_descriptor_field_identifier
HV00B fdw_invalid_handle
HV00C fdw_invalid_option_index
HV00D fdw_invalid_option_name
HV090 fdw_invalid_string_length_or_buffer_length
HV00A fdw_invalid_string_format
HV009 fdw_invalid_use_of_null_pointer
HV014 fdw_too_many_handles
HV001 fdw_out_of_memory
HV00P fdw_no_schemas
HV00J fdw_option_name_not_found
HV00K fdw_reply_handle
HV00Q fdw_schema_not_found
HV00R fdw_table_not_found
HV00L fdw_unable_to_create_execution
HV00M fdw_unable_to_create_reply
HV00N fdw_unable_to_establish_connection
Class P0 — PL/pgSQL Error
P0000 plpgsql_error
P0001 raise_exception
P0002 no_data_found
P0003 too_many_rows
P0004 assert_failure
Class XX — Internal Error
XX000 internal_error
XX001 data_corrupted
XX002 index_corrupted
Prev Up Next
Part VIII. Appendixes Home Appendix B. Date/Time Support

Submit correction

If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.

Источник

I’m trying to learn PostgreSQL stored procedures. Specifically creating the procedure below in PSQL.

CREATE OR REPLACE PROCEDURE BUILD_AND_POPULATE(INOUT cresults refcursor) 
    LANGUAGE PLPGSQL 
    AS $$
    BEGIN
        BEGIN; -- I've tried removing this but the behaviour is the same
        cresults:= 'cur';
        DROP TABLE IF EXISTS procsampledata;
        CREATE TABLE procsampledata as select x,1 as c2,2 as c3, md5(random()::text) from generate_series(1,10) x;  
        COMMIT;
        OPEN cresults FOR SELECT * FROM procsampledata;  
    END;
$$;

Then I execute it like so, but receive an error:

postgres=# call build_and_populate(null);
ERROR:  invalid transaction termination
CONTEXT:  PL/pgSQL function build_and_populate(refcursor) line 6 at COMMIT

I’ve tried with AUTOCOMMIT set to both on and off.

This is my Postgres version

 PostgreSQL 11.5 on x86_64-pc-linux-musl, compiled by gcc (Alpine 8.3.0) 8.3.0, 64-bit

Can anyone see what I’m doing wrong? Thank you!

People have blogged about the fact that commit is legal in a PostgreSQL stored proc, starting with version 11—which is what yugabyteDB Version 1.3.0 uses. Here’s an example:

PostgreSQL 11 – Server-side Procedures (Part 1)

However, these blogs forget that psql (and therefore ysqlsh) have AUTOCOMMIT set to True by default. They seem to do their tests in that mode and forget that the AUTOCOMMIT mode might be False. However, things change when the AUTOCOMMIT mode is False. See the following testcase.

I would like to hear the explanation for this weird behavior. It’s add odds with the definition of autocommitting: if the insert has autocommitted, then by definition there’s nothing to commit or rollback.

Following on from this, I see no reason why call my_proc() has to start a txn. It might do no more than a calculation. As I see it, it’s the first SQL statement that’s executed from the stored proc that should start a txn. Notice that within the present regime, and given that the weakest isolation level in yugabyte DB is repeatable read, you cannot implement a stored proc to watch a table in an infinite loop and to respond when it suffers an interesting change. Unless, that is, the non-intuitive workaround that I show below helps there.

Note: this remains to be tested.

i psqlrc
set AUTOCOMMIT False
begin;
drop table if exists marbles;
commit;

begin;
create table marbles(
  pk integer
    constraint t_pk primary key,
  color text
    constraint t_c1_nn not null
    constraint t_c1_chk check(color in ('black', 'white'))
  );
commit;

begin;
insert into marbles(pk, color) values
  (1, 'black'),
  (2, 'white');
commit;

begin;
drop procedure if exists set_all_marbles_black();
commit;

begin;
create procedure set_all_marbles_black()
  language plpgsql
as $$
  begin
    -- The "start transaction" and "set transaction" errors can be
    -- understood in the context that "call" starts a transaction, if
    -- one isn't already ongoing.

    -- Commented out 'cos causes run-time error
    -- "0A000: unsupported transaction command in PL/pgSQL".
    -- start transaction;

    -- Commented out 'cos causes run-time error
    -- "25001: SET TRANSACTION ISOLATION LEVEL must be called before any query"
    -- Yet, as a top-level command, it *does* start a txn.
    -- set transaction isolation level serializable;

    update marbles set color = 'black' where color = 'white';

    -- It's hard to understand the thinking that requires this error.
    -- Especially given that it occurs when AUTOCOMMIT is False
    -- but not when it's True.
    -- Causes "2D000: invalid transaction termination"
    rollback;
  end;
$$;
commit;

select pk, color from marbles order by pk;
rollback;

set AUTOCOMMIT True
-- Runs OK, and the "rollback" has its effect.
call set_all_marbles_black();
select pk, color from marbles order by pk;

set AUTOCOMMIT False
-- This causes:
--   ysql:plpgsql_proc_txn_control.sql:63: ERROR:  invalid transaction termination
--   CONTEXT:  PL/pgSQL function set_all_marbles_black() line 21 at ROLLBACK
call set_all_marbles_black();

-- Must rollback before trying anything else.
rollback;

Here, for completeness, is another manifestation of the same issue. This quietly succeeds:

set AUTOCOMMIT True
do $$
begin
  update marbles set color = 'black' where color = 'white';
  commit;
end
$$;

But this fails with "ERROR: invalid transaction termination":

set AUTOCOMMIT False
do $$
begin
  update marbles set color = 'black' where color = 'white';
  commit;
end
$$;

Hi Jagmohan,

What is the point of COMMITting after a few records? Why not let the
whole batch run through. If there are any errors, PostgreSQL will
rollback the whole transaction anyway.

As opposed to Oracle, PostgreSQL won’t commit some records, while others
failed within the same transaction. As soon as a single operation within
a transaction fails, it’s dead and all subsequent operations will fail.

Regards,

Holger

Am 23.11.20 um 18:05 schrieb Jagmohan Kaintura:
> Hi Team,
>
> We have many BATCH JOBS in Oracle which we are committing after
> processing few Records. These batch Jobs process in some subsets and
> call transaction control statements COMMIT in case of Success and
> ROLLBACK in case of failure.
>
> While converting to POstgreSQL we converted in Same Format with COMMIT
> and ROLLBACK. But while executing it ended up with below error message.
> ERROR:  invalid transaction termination
> CONTEXT:  PL/pgSQL function inline_code_block line 29 at COMMIT
>
> While reviewing the Transaction Management in PostgreSQL 
> «https://www.postgresql.org/docs/12/plpgsql-transactions.html
> <https://www.postgresql.org/docs/12/plpgsql-transactions.html>» it
> speaks about a format which is not Supported.
>
> Transaction control is only possible in |CALL| or |DO| invocations
> from the top level or nested |CALL| or |DO| invocations without any
> other intervening command. For example, if the call stack is |CALL
> proc1()| → |CALL proc2()| → |CALL proc3()|, then the second and third
> procedures can perform transaction control actions. But if the call
> stack is |CALL proc1()| → |SELECT func2()| → |CALL proc3()|, then the
> last procedure cannot do transaction control, because of the
> |SELECT| in between.
>
> My Call has :  CALL Batch Job => SELECT function Used in SQL
> Statements  ==> Call Procedure.  We have transaction control in «CALL
> Batch Job» only.
>
> Pseudo Code is like : Highlighted in BOLD is a function call. It’s
> failing when getting executed as we are using functions into this
> procedure.
> *Can any help on this matter , how I can implement Batch Jobs as we
> wanted to commit in few intervals of 20000 records ?*
> We can’t remove this function from the statement as its value is
> dependent on column value.
>
> CREATE OR REPLACE PROCEDURE TEST_TRANSACTION(
> )
> LANGUAGE ‘plpgsql’
>     SECURITY DEFINER
> AS $BODY$
> DECLARE
>     G_LAST_UPDATE_USER_SYSTEM VARCHAR2(6) := ‘SYSTEM’;
>     G_LAST_UPDATE_MODULE_BATCH VARCHAR2(5) := ‘BATCH’;
>
>     G_CNTR_LOADING_EXPIRED TMS_CONTAINER_LOADING.STATUS_CODE%TYPE := ’90’;
>     G_DG_MANIFEST_DELETED TMS_DG_MANIFEST.STATUS_CODE%TYPE := ’80’;
>     G_DG_MANIFEST_COMPLETED TMS_DG_MANIFEST.STATUS_CODE%TYPE := ’95’;
>
>     v_num_day numeric;
>     v_batch_count numeric;
>     v_log_count numeric := 0;
>     v_local_batch_count numeric;
> BEGIN
>         v_batch_count := 0;
>         LOOP
>             update tms_container_loading
>                set status_code = G_CNTR_LOADING_EXPIRED
>                  , last_update_tm = clock_timestamp()::timestamp(0)
>                  , last_update_user_an = G_LAST_UPDATE_USER_SYSTEM
>                  , last_update_module_code = G_LAST_UPDATE_MODULE_BATCH
>              where
> *tms$vvd.is_vessel_departed(vessel_visit_c,trunc(clock_timestamp()::timestamp(0)))* =
> 1
>                and coalesce(status_code,’~’) <> G_CNTR_LOADING_EXPIRED
>  and ctid in (select a.ctid from tms_container_loading  where
> *tms$vvd.is_vessel_departed(vessel_visit_c,trunc(clock_timestamp()::timestamp(0)))* =
> 1
>                and coalesce(status_code,’~’) <> G_CNTR_LOADING_EXPIRED
> LIMIT 20000);
>             EXIT WHEN NOT FOUND; /* apply on SQL */
>             GET DIAGNOSTICS v_local_batch_count = ROW_COUNT;
> v_batch_count := v_batch_count + v_local_batch_count;
>             COMMIT;
>         END LOOP;
>         v_log_count := v_log_count + 1; CALL
> Log(v_batch_count,’TMS_CONTAINER_LOADING’,NULL, ‘TMS$BATCH_JOB’,
> v_log_count);
>         COMMIT;
> END;
> $BODY$;
>
> *Best Regards,*
> Jagmohan


Holger Jakobs, Bergisch Gladbach, Tel. +49-178-9759012

Studying Stored Procs in Postgres 11

With Postgres 11 looming on the near horizon, it’s only appropriate to check out a recent beta and kick the tires a few times. Whether it’s improvements in parallelism, partitions, stored procedures, JIT functionality, or any number of elements in the release page, there’s a lot to investigate.

It just so happens that I ran across a fortuitous event on Twitter when deciding on an appropriate topic. Behold!

waste_xid Postgres 11

Wasting XIDs has never been this fun

Wait! No! That’s not what stored procedures are for!

I felt so good like anything was possible

When confronted with such a blatant corruption of such a cool new feature, it’s only natural to question the wisdom of doing so. It is, after all, not a great idea to programatically consume transaction IDs. I said as much and moved on with life, certain the worst was over.

Then this happened.

waste_xid is fun

Magnus helpfully recommends proceeding

Now, Magnus is infamous for two things: his technical acumen, and giddy malevolence. His advocacy of a stored procedure named “waste_xid” only proved nobody anywhere should ever run this anywhere, lest they immolate whatever system hosted the database instance.

But hey, VMs are cheap; let’s break things. How else can we learn the limits of our new toys, but by virtually atomizing them?

I hit cruise control and rubbed my eyes

Before we saddle our poor innocent Postgres 11 installation with an inadvisable stored procedure designed specifically to underhandedly reap its transaction lifespan, we should probably make the routine as evil as possible.

One thing stands out immediately: calling EXECUTE is unnecessary overhead. According to the information function documentation, txid_current will assign a new transaction ID if there isn’t one already. Since the stored procedure is constantly committing, that’s extremely handy. And since this is Pl/pgSQL, we can use direct assignment instead.

Our new procedure looks something like this:

CREATE OR REPLACE PROCEDURE waste_xid(cnt int)
AS $$
DECLARE
    i INT;
    x BIGINT;
BEGIN
    FOR i in 1..cnt LOOP
        x := txid_current();
        COMMIT;
    END LOOP;
END;
$$
LANGUAGE plpgsql;

Great! We can now waste XIDs about 4x faster than before! On this particular test VM, the maximum amount of wasted XIDs per second was about 125k. At that rate, we could blow through a billion in roughly two hours.

Since quite a few installations use the default autovacuum settings, that means we could trigger a lot of vacuums at the 200M transaction mark in about half an hour. We didn’t need that disk throughput anyway.

Workin’ on a mystery, goin’ wherever it leads

Regardless, what happens if we run it?

CALL waste_xid(1000000);

CALL

Well, that was anticlimactic. How about the logs? We set our min_log_duration_statement to 100ms for debugging purposes, so we should definitely see a long call there.

2018-10-03 20:53:02.505 UTC [11334] [email protected] LOG:  duration: 7949.540 ms  statement: call waste_xid(1000000);

Here’s something that’s actually more interesting than it might appear at first glance. Stored procedures, it would seem, are atomic. We tested explicitly by adding a long pg_sleep call in the loop, and none of those entries were logged.

Since procedures can COMMIT transactions and thus could contain semantically unrelated unrelated activity to the originating transaction, can a procedure essentially derail an existing transaction? Let’s see:

BEGIN;

CALL waste_xid(1000000);

ERROR:  invalid transaction termination
CONTEXT:  PL/pgSQL function waste_xid(integer) line 8 at COMMIT

Nope! Apparently CALL is only valid within its own context; no transactions for you. This could introduce interesting behavior in application layers that implicitly start a transaction before every command. The resulting error is a bit ambiguous however; why is the transaction termination invalid? Can’t it just say we can’t close the current transaction from within a procedure?

So stored procedures are atomic, and can do whatever they want in their own transactional context. What else are they hiding?

I put the pedal down to make some time

It seems there are special rules to how transactions are handled in stored procedures. While the discussion was hot, a couple more functional variants popped up. Here’s an interesting one:

CREATE OR REPLACE PROCEDURE check_now()
AS $$
DECLARE
    i int;
BEGIN
    FOR i in 1..5 LOOP
        RAISE NOTICE 'It is now: %', now();
        PERFORM txid_current();
        COMMIT;
        PERFORM pg_sleep(0.1);
    END LOOP;
END;
$$
LANGUAGE plpgsql;

What are we testing for here? Well, one fundamental attribute of the now() function and several other related DATETIME routines, is that they are tied to the current transaction. So in theory, the value being returned should change.

Let’s try it:

CALL check_now();

NOTICE:  It is now: 2018-10-03 21:27:07.764822+00
NOTICE:  It is now: 2018-10-03 21:27:07.764822+00
NOTICE:  It is now: 2018-10-03 21:27:07.764822+00
NOTICE:  It is now: 2018-10-03 21:27:07.764822+00
NOTICE:  It is now: 2018-10-03 21:27:07.764822+00

Uh oh. What does this actually mean? Is now() being tethered to the initial CALL instead of the current transaction? Or are PL/pgSQL transactions not real transactions? If they do operate in a different context than the outer transaction, how else does that context diverge? Or is this a bug?

Postgres 11 is still in beta, after all.

It was always cold, no sunshine

Upon adopting the PROCEDURE nomenclature, we’re also presented with a rather unique historical quirk. Consider the previous syntax for creating a trigger. More than a few triggers have been declared this way:

CREATE TRIGGER t_my_trigger
BEFORE INSERT OR UPDATE ON mytable
   FOR EACH ROW
       EXECUTE PROCEDURE my_function();

Ever prescient, the devs saw this coming and added a disclaimer to the Postgres 11 docs:

In the syntax of CREATE TRIGGER, the keywords FUNCTION and PROCEDURE are equivalent, but the referenced function must in any case be a function, not a procedure. The use of the keyword PROCEDURE here is historical and deprecated.

So in case you’d mentally linked the historical—and up until Postgres 11, current—syntax with the new stored procedure functionality, don’t. Procedures really did change more of the Postgres internals than a cursory examination could reveal.

There’s something good waitin’ down this road

Now that we’ve kicked the proverbial tires and explored a few of the neat quirks stored procedures offer, what does it all mean?

First and foremost, the ability to COMMIT or ROLLBACK within a function call allows both safeguarding activity batches or particularly critical data. No longer will one function call represent an all-or-nothing proposition. That alone is a massive enhancement to Postgres, one which users of other database engines such as Oracle have been clamoring for over the last 20 years.

More subtly, it presents a new security concern that administrators should consider. The ability to manipulate transactions is a powerful lever ripe for abuse. At minimum, DBAs should consider revoking USAGE on any languages from users that shouldn’t be granted that capability.

REVOKE USAGE ON LANGUAGE plpgsql FROM PUBLIC;

Users will still be able to invoke previously defined functions or procedures, but won’t be able to create new ones. In fact, this should probably be done on all current systems as a matter of course.

Judiciously wielded, Postgres can continue runnin’ down the dream of becoming the best and most feature-laden database. We just need to pay attention to where we’re going.

Содержание

  1. Обсуждение: ERROR : invalid transaction termination : PostgreSQL v12
  2. ERROR : invalid transaction termination : PostgreSQL v12
  3. Re: ERROR : invalid transaction termination : PostgreSQL v12
  4. Недействительное завершение транзакции
  5. 1 ответ
  6. Thread: ERROR : invalid transaction termination : PostgreSQL v12
  7. ERROR : invalid transaction termination : PostgreSQL v12
  8. Re: ERROR : invalid transaction termination : PostgreSQL v12
  9. Please explain the inscrutable rules for when «commit» in a stored proc succeeds or causes a run-time error #1957
  10. Comments
  11. bllewell commented Aug 2, 2019 •
  12. Недействительное завершение транзакции
  13. 1 ответ

Обсуждение: ERROR : invalid transaction termination : PostgreSQL v12

ERROR : invalid transaction termination : PostgreSQL v12

CREATE OR REPLACE PROCEDURE TEST_TRANSACTION(
)
LANGUAGE ‘plpgsql’
SECURITY DEFINER
AS $BODY$
DECLARE
G_LAST_UPDATE_USER_SYSTEM VARCHAR2(6) := ‘SYSTEM’;
G_LAST_UPDATE_MODULE_BATCH VARCHAR2(5) := ‘BATCH’;

G_CNTR_LOADING_EXPIRED TMS_CONTAINER_LOADING.STATUS_CODE%TYPE := ’90’;
G_DG_MANIFEST_DELETED TMS_DG_MANIFEST.STATUS_CODE%TYPE := ’80’;
G_DG_MANIFEST_COMPLETED TMS_DG_MANIFEST.STATUS_CODE%TYPE := ’95’;

v_num_day numeric;
v_batch_count numeric;
v_log_count numeric := 0;
v_local_batch_count numeric;
BEGIN
v_batch_count := 0;
LOOP
update tms_container_loading
set status_code = G_CNTR_LOADING_EXPIRED
, last_update_tm = clock_timestamp()::timestamp(0)
, last_update_user_an = G_LAST_UPDATE_USER_SYSTEM
, last_update_module_code = G_LAST_UPDATE_MODULE_BATCH
where tms$vvd.is_vessel_departed(vessel_visit_c,trunc(clock_timestamp()::timestamp(0))) = 1
and coalesce(status_code,’

‘) <> G_CNTR_LOADING_EXPIRED and ctid in (select a.ctid from tms_container_loading where tms$vvd.is_vessel_departed(vessel_visit_c,trunc(clock_timestamp()::timestamp(0))) = 1
and coalesce(status_code,’

‘) <> G_CNTR_LOADING_EXPIRED LIMIT 20000);
EXIT WHEN NOT FOUND; /* apply on SQL */
GET DIAGNOSTICS v_local_batch_count = ROW_COUNT; v_batch_count := v_batch_count + v_local_batch_count;
COMMIT;
END LOOP;
v_log_count := v_log_count + 1; CALL Log(v_batch_count,’TMS_CONTAINER_LOADING’,NULL, ‘TMS$BATCH_JOB’, v_log_count);
COMMIT;
END;
$BODY$;

Re: ERROR : invalid transaction termination : PostgreSQL v12

What is the point of COMMITting after a few records? Why not let the whole batch run through. If there are any errors, PostgreSQL will rollback the whole transaction anyway.

As opposed to Oracle, PostgreSQL won’t commit some records, while others failed within the same transaction. As soon as a single operation within a transaction fails, it’s dead and all subsequent operations will fail.

CREATE OR REPLACE PROCEDURE TEST_TRANSACTION(
)
LANGUAGE ‘plpgsql’
SECURITY DEFINER
AS $BODY$
DECLARE
G_LAST_UPDATE_USER_SYSTEM VARCHAR2(6) := ‘SYSTEM’;
G_LAST_UPDATE_MODULE_BATCH VARCHAR2(5) := ‘BATCH’;

G_CNTR_LOADING_EXPIRED TMS_CONTAINER_LOADING.STATUS_CODE%TYPE := ’90’;
G_DG_MANIFEST_DELETED TMS_DG_MANIFEST.STATUS_CODE%TYPE := ’80’;
G_DG_MANIFEST_COMPLETED TMS_DG_MANIFEST.STATUS_CODE%TYPE := ’95’;

v_num_day numeric;
v_batch_count numeric;
v_log_count numeric := 0;
v_local_batch_count numeric;
BEGIN
v_batch_count := 0;
LOOP
update tms_container_loading
set status_code = G_CNTR_LOADING_EXPIRED
, last_update_tm = clock_timestamp()::timestamp(0)
, last_update_user_an = G_LAST_UPDATE_USER_SYSTEM
, last_update_module_code = G_LAST_UPDATE_MODULE_BATCH
where tms$vvd.is_vessel_departed(vessel_visit_c,trunc(clock_timestamp()::timestamp(0))) = 1
and coalesce(status_code,’

‘) <> G_CNTR_LOADING_EXPIRED and ctid in (select a.ctid from tms_container_loading where tms$vvd.is_vessel_departed(vessel_visit_c,trunc(clock_timestamp()::timestamp(0))) = 1
and coalesce(status_code,’

‘) <> G_CNTR_LOADING_EXPIRED LIMIT 20000);
EXIT WHEN NOT FOUND; /* apply on SQL */
GET DIAGNOSTICS v_local_batch_count = ROW_COUNT; v_batch_count := v_batch_count + v_local_batch_count;
COMMIT;
END LOOP;
v_log_count := v_log_count + 1; CALL Log(v_batch_count,’TMS_CONTAINER_LOADING’,NULL, ‘TMS$BATCH_JOB’, v_log_count);
COMMIT;
END;
$BODY$;

Источник

Недействительное завершение транзакции

У меня есть процедура, которая, когда я выполняю в dbeaver, отлично работает без проблем, однако, когда я вызываю ее из внешней программы, я получаю сообщение об ошибке ниже. Я не хочу копировать / вставлять здесь полную процедуру, потому что она довольно большая и работает в инструменте db. Я просто копирую / вставляю верх и низ. Что могло быть причиной этого?

Процедура:

Ошибка:

1 ответ

Управление транзакциями возможно только в вызовах CALL или DO с верхнего уровня или вложенных вызовах CALL или DO без какой-либо другой промежуточной команды. Например, если стек вызовов CALL proc1() → CALL proc2() → CALL proc3() , то вторая и третья процедуры могут выполнять действия по управлению транзакциями. Но если стек вызовов CALL proc1() → SELECT func2() → CALL proc3() , то последняя процедура не может управлять транзакцией из-за SELECT между ними.

Есть и другие, недокументированные ограничения:

Вы не можете явно начать транзакцию с помощью BEGIN и зафиксировать ее внутри транзакции. Таким образом, следующее не удастся:

Это может быть улучшено в будущих выпусках.

Все процедуры в стеке вызовов должны быть написаны на PL / pgSQL:

Как бы то ни было, контроль транзакций внутри процедур PostgreSQL несколько ограничен.

Если вы нарушите какое-либо из этих правил, вы получите сообщение об ошибке, описанное в вашем вопросе. Вероятно, вам придется обрабатывать транзакции в приложении, а не в процедуре — возможно, разделение процедуры на более мелкие части делает это возможным.

Источник

Thread: ERROR : invalid transaction termination : PostgreSQL v12

ERROR : invalid transaction termination : PostgreSQL v12

CREATE OR REPLACE PROCEDURE TEST_TRANSACTION(
)
LANGUAGE ‘plpgsql’
В В SECURITY DEFINER
AS $BODY$
DECLARE
В В G_LAST_UPDATE_USER_SYSTEM VARCHAR2(6) := ‘SYSTEM’;
В В G_LAST_UPDATE_MODULE_BATCH VARCHAR2(5) := ‘BATCH’;

В В G_CNTR_LOADING_EXPIRED TMS_CONTAINER_LOADING.STATUS_CODE%TYPE := ’90’;
В В G_DG_MANIFEST_DELETED TMS_DG_MANIFEST.STATUS_CODE%TYPE := ’80’;
В В G_DG_MANIFEST_COMPLETED TMS_DG_MANIFEST.STATUS_CODE%TYPE := ’95’;

В В v_num_day numeric;
В В v_batch_count numeric;
В В v_log_count numeric := 0;
В В v_local_batch_count numeric;
BEGIN
В В В В v_batch_count := 0;
В В В В LOOP
В В В В В В update tms_container_loading
В В В В В В В В set status_code = G_CNTR_LOADING_EXPIRED
В В В В В В В В В , last_update_tm = clock_timestamp()::timestamp(0)
В В В В В В В В В , last_update_user_an = G_LAST_UPDATE_USER_SYSTEM
В В В В В В В В В , last_update_module_code = G_LAST_UPDATE_MODULE_BATCH
В В В В В В В whereВ tms$vvd.is_vessel_departed(vessel_visit_c,trunc(clock_timestamp()::timestamp(0)))В = 1
В В В В В В В В and coalesce(status_code,’

‘) <> G_CNTR_LOADING_EXPIRED В and ctid in (select a.ctid from tms_container_loading В whereВ tms$vvd.is_vessel_departed(vessel_visit_c,trunc(clock_timestamp()::timestamp(0)))В = 1
В В В В В В В В and coalesce(status_code,’

‘) <> G_CNTR_LOADING_EXPIRED LIMIT 20000);
В В В В В В EXIT WHEN NOT FOUND; /* apply on SQL */
В В В В В В GET DIAGNOSTICS v_local_batch_count = ROW_COUNT; v_batch_count := v_batch_count + v_local_batch_count;
В В В В В В COMMIT;
В В В В END LOOP;
В В В В v_log_count := v_log_count + 1; CALL Log(v_batch_count,’TMS_CONTAINER_LOADING’,NULL, ‘TMS$BATCH_JOB’, v_log_count);
В В В В COMMIT;
END;
$BODY$;

Re: ERROR : invalid transaction termination : PostgreSQL v12

What is the point of COMMITting after a few records? Why not let the whole batch run through. If there are any errors, PostgreSQL will rollback the whole transaction anyway.

As opposed to Oracle, PostgreSQL won’t commit some records, while others failed within the same transaction. As soon as a single operation within a transaction fails, it’s dead and all subsequent operations will fail.

CREATE OR REPLACE PROCEDURE TEST_TRANSACTION(
)
LANGUAGE ‘plpgsql’
В В SECURITY DEFINER
AS $BODY$
DECLARE
В В G_LAST_UPDATE_USER_SYSTEM VARCHAR2(6) := ‘SYSTEM’;
В В G_LAST_UPDATE_MODULE_BATCH VARCHAR2(5) := ‘BATCH’;

В В G_CNTR_LOADING_EXPIRED TMS_CONTAINER_LOADING.STATUS_CODE%TYPE := ’90’;
В В G_DG_MANIFEST_DELETED TMS_DG_MANIFEST.STATUS_CODE%TYPE := ’80’;
В В G_DG_MANIFEST_COMPLETED TMS_DG_MANIFEST.STATUS_CODE%TYPE := ’95’;

В В v_num_day numeric;
В В v_batch_count numeric;
В В v_log_count numeric := 0;
В В v_local_batch_count numeric;
BEGIN
В В В В v_batch_count := 0;
В В В В LOOP
В В В В В В update tms_container_loading
В В В В В В В В set status_code = G_CNTR_LOADING_EXPIRED
В В В В В В В В В , last_update_tm = clock_timestamp()::timestamp(0)
В В В В В В В В В , last_update_user_an = G_LAST_UPDATE_USER_SYSTEM
В В В В В В В В В , last_update_module_code = G_LAST_UPDATE_MODULE_BATCH
В В В В В В В whereВ tms$vvd.is_vessel_departed(vessel_visit_c,trunc(clock_timestamp()::timestamp(0)))В = 1
В В В В В В В В and coalesce(status_code,’

‘) <> G_CNTR_LOADING_EXPIRED В and ctid in (select a.ctid from tms_container_loading В whereВ tms$vvd.is_vessel_departed(vessel_visit_c,trunc(clock_timestamp()::timestamp(0)))В = 1
В В В В В В В В and coalesce(status_code,’

‘) <> G_CNTR_LOADING_EXPIRED LIMIT 20000);
В В В В В В EXIT WHEN NOT FOUND; /* apply on SQL */
В В В В В В GET DIAGNOSTICS v_local_batch_count = ROW_COUNT; v_batch_count := v_batch_count + v_local_batch_count;
В В В В В В COMMIT;
В В В В END LOOP;
В В В В v_log_count := v_log_count + 1; CALL Log(v_batch_count,’TMS_CONTAINER_LOADING’,NULL, ‘TMS$BATCH_JOB’, v_log_count);
В В В В COMMIT;
END;
$BODY$;

Источник

Please explain the inscrutable rules for when «commit» in a stored proc succeeds or causes a run-time error #1957

People have blogged about the fact that commit is legal in a PostgreSQL stored proc, starting with version 11—which is what yugabyteDB Version 1.3.0 uses. Here’s an example:

However, these blogs forget that psql (and therefore ysqlsh ) have AUTOCOMMIT set to True by default. They seem to do their tests in that mode and forget that the AUTOCOMMIT mode might be False . However, things change when the AUTOCOMMIT mode is False . See the following testcase.

I would like to hear the explanation for this weird behavior. It’s add odds with the definition of autocommitting: if the insert has autocommitted, then by definition there’s nothing to commit or rollback .

Following on from this, I see no reason why call my_proc() has to start a txn. It might do no more than a calculation. As I see it, it’s the first SQL statement that’s executed from the stored proc that should start a txn. Notice that within the present regime, and given that the weakest isolation level in yugabyte DB is repeatable read , you cannot implement a stored proc to watch a table in an infinite loop and to respond when it suffers an interesting change. Unless, that is, the non-intuitive workaround that I show below helps there.

Note: this remains to be tested.

Here, for completeness, is another manifestation of the same issue. This quietly succeeds:

But this fails with «ERROR: invalid transaction termination» :

The text was updated successfully, but these errors were encountered:

Источник

Недействительное завершение транзакции

У меня есть процедура, которая, когда я выполняю в dbeaver, отлично работает без проблем, однако, когда я вызываю ее из внешней программы, я получаю сообщение об ошибке ниже. Я не хочу копировать / вставлять здесь полную процедуру, потому что она довольно большая и работает в инструменте db. Я просто копирую / вставляю верх и низ. Что могло быть причиной этого?

Процедура:

Ошибка:

1 ответ

Управление транзакциями возможно только в вызовах CALL или DO с верхнего уровня или вложенных вызовах CALL или DO без какой-либо другой промежуточной команды. Например, если стек вызовов CALL proc1() → CALL proc2() → CALL proc3() , то вторая и третья процедуры могут выполнять действия по управлению транзакциями. Но если стек вызовов CALL proc1() → SELECT func2() → CALL proc3() , то последняя процедура не может управлять транзакцией из-за SELECT между ними.

Есть и другие, недокументированные ограничения:

Вы не можете явно начать транзакцию с помощью BEGIN и зафиксировать ее внутри транзакции. Таким образом, следующее не удастся:

Это может быть улучшено в будущих выпусках.

Все процедуры в стеке вызовов должны быть написаны на PL / pgSQL:

Как бы то ни было, контроль транзакций внутри процедур PostgreSQL несколько ограничен.

Если вы нарушите какое-либо из этих правил, вы получите сообщение об ошибке, описанное в вашем вопросе. Вероятно, вам придется обрабатывать транзакции в приложении, а не в процедуре — возможно, разделение процедуры на более мелкие части делает это возможным.

Источник


  1. Home


  2. SQL, Database Testing


  3. «invalid transaction termination» in Postgres when trying to update a value using a function

This topic has been deleted. Only users with topic management privileges can see it.


  • I’m new to postgres. I defined a function like this:

    CREATE OR REPLACE FUNCTION increment_count() 
      RETURNS TRIGGER LANGUAGE plpgsql 
      AS $$
       BEGIN 
         UPDATE posts SET count=count+1 WHERE pid=NEW.pid;
       COMMIT;
       END;
      $$;
    

    This gives me error invalid transaction termination


  • You can not commit in a trigger, and you forgot the return statement.

    CREATE OR REPLACE FUNCTION increment_count() 
      RETURNS TRIGGER LANGUAGE plpgsql 
      AS $$
        BEGIN 
          UPDATE posts SET count=count+1 WHERE pid=NEW.pid;
          return null;
        END;
      $$;
    

    In case of a BEFORE trigger, «return NEW» should be used!


Suggested Topics

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    1
    Views

  • 2

    0
    Votes

    2
    Posts

    1
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

  • 2

    0
    Votes

    2
    Posts

    0
    Views

Понравилась статья? Поделить с друзьями:
  • Error invalid token error description token is incorrect перевод
  • Error invalid texture index 3ds max
  • Error invalid terms in product
  • Error invalid suffix x on integer constant
  • Error invalid subscript selector maple