Thread: Code checks for App Devs, using new options for transaction behavior

Code checks for App Devs, using new options for transaction behavior

From
Simon Riggs
Date:
In the past, developers have wondered how we can provide "--dry-run"
functionality
https://www.postgresql.org/message-id/15791.1450383201%40sss.pgh.pa.us

This is important for application developers, especially when
migrating programs to Postgres.

Presented here are 3 features aimed at developers, each of which is
being actively used by me in a large and complex migration project.

* psql --parse-only
Checks the syntax of all SQL in a script, but without actually
executing it. This is very important in the early stages of complex
migrations because we need to see if the code would generate syntax
errors before we attempt to execute it. When there are many
dependencies between objects, actual execution fails very quickly if
we run in a single transaction, yet running outside of a transaction
can leave a difficult cleanup task. Fixing errors iteratively is
difficult when there are long chains of dependencies between objects,
since there is no easy way to predict how long it will take to make
everything work unless you understand how many syntax errors exist in
the script.
001_psql_parse_only.v1.patch

* nested transactions = off (default) | all | on
Handle nested BEGIN/COMMIT, which can cause chaos on failure. This is
an important part of guaranteeing that everything that gets executed
is part of a single atomic transaction, which can then be rolled back
- this is a pre-requisite for the last feature.
002_nested_xacts.v7.patch
The default behavior is unchanged (off)
Setting "all" treats nested BEGIN/COMMIT as subtransactions, allowing
some parts to fail without rolling back the outer transaction.
Setting "outer" flattens nested BEGIN/COMMIT into one single outer
transaction, so that any failure rolls back the entire transaction.

* rollback_on_commit = off (default) | on
Force transactions to fail their final commit, ensuring that no
lasting change is made when a script is tested. i.e. accept COMMIT,
but do rollback instead.
003_rollback_on_commit.v1.patch

We will probably want to review these on separate threads, but the
common purpose of these features is hopefully clear from these notes.

001 and 003 are fairly small patches, 002 is longer.

Comments please

-- 
Simon Riggs                http://www.EnterpriseDB.com/

Attachment

Re: Code checks for App Devs, using new options for transaction behavior

From
Simon Riggs
Date:
On Thu, 27 Oct 2022 at 12:09, Simon Riggs <simon.riggs@enterprisedb.com> wrote:

> Comments please

Update from patch tester results.

-- 
Simon Riggs                http://www.EnterpriseDB.com/

Attachment

Re: Code checks for App Devs, using new options for transaction behavior

From
Erik Rijkers
Date:
Op 27-10-2022 om 18:35 schreef Simon Riggs:
> On Thu, 27 Oct 2022 at 12:09, Simon Riggs <simon.riggs@enterprisedb.com> wrote:
> 
>> Comments please
> 
> Update from patch tester results.
> 

 > [001_psql_parse_only.v1.patch     ]
 > [002_nested_xacts.v7.patch        ]
 > [003_rollback_on_commit.v1.patch  ]
 > [004_add_params_to_sample.v1.patch]


patch 002 has (2x) :
   'transction'  should be
   'transaction'

also in patch 002:
   'at any level will be abort'  should be
   'at any level will abort'

I also dislike the 'we' in

   'Once we reach the top-level transaction,'

That seems a bit too much like the 'we developers working together to 
make a database server system' which is of course used often and 
usefully on this mailinglist and in code itself.  But I think 
user-facing docs should be careful with that team-building 'we'.  I 
remember well how it confused me, many years ago.  Better, IMHO:

   'Once the top-level transaction is reached,'


Thanks,

Erik Rijkers



Re: Code checks for App Devs, using new options for transaction behavior

From
Simon Riggs
Date:
On Fri, 28 Oct 2022 at 07:54, Erik Rijkers <er@xs4all.nl> wrote:
>
> Op 27-10-2022 om 18:35 schreef Simon Riggs:
> > On Thu, 27 Oct 2022 at 12:09, Simon Riggs <simon.riggs@enterprisedb.com> wrote:
> >
> >> Comments please
> >
> > Update from patch tester results.
> >
>
>  > [001_psql_parse_only.v1.patch     ]
>  > [002_nested_xacts.v7.patch        ]
>  > [003_rollback_on_commit.v1.patch  ]
>  > [004_add_params_to_sample.v1.patch]
>
>
> patch 002 has (2x) :
>    'transction'  should be
>    'transaction'
>
> also in patch 002:
>    'at any level will be abort'  should be
>    'at any level will abort'
>
> I also dislike the 'we' in
>
>    'Once we reach the top-level transaction,'
>
> That seems a bit too much like the 'we developers working together to
> make a database server system' which is of course used often and
> usefully on this mailinglist and in code itself.  But I think
> user-facing docs should be careful with that team-building 'we'.  I
> remember well how it confused me, many years ago.  Better, IMHO:
>
>    'Once the top-level transaction is reached,'

Thanks for the feedback, I will make all of those corrections in the
next version.

I'm guessing you like the features??

-- 
Simon Riggs                http://www.EnterpriseDB.com/



Re: Code checks for App Devs, using new options for transaction behavior

From
Simon Riggs
Date:
On Fri, 28 Oct 2022 at 10:33, Simon Riggs <simon.riggs@enterprisedb.com> wrote:

> Thanks for the feedback, I will make all of those corrections in the
> next version.

New version attached. I've rolled 002-004 into one patch, but can
split again as needed.

-- 
Simon Riggs                http://www.EnterpriseDB.com/

Attachment

Re: Code checks for App Devs, using new options for transaction behavior

From
Dilip Kumar
Date:
On Sun, Oct 30, 2022 at 11:32 PM Simon Riggs
<simon.riggs@enterprisedb.com> wrote:
>
> On Fri, 28 Oct 2022 at 10:33, Simon Riggs <simon.riggs@enterprisedb.com> wrote:
>
> > Thanks for the feedback, I will make all of those corrections in the
> > next version.
>
> New version attached. I've rolled 002-004 into one patch, but can
> split again as needed.

I like the idea of "parse only" and "nested xact", thanks for working
on this.  I will look into patches in more detail, especially nested
xact. IMHO there is no point in merging "nested xact" and "rollback on
commit". They might be changing the same code location but these two
are completely different ideas, in fact all these three should be
reviewed as three separate threads as you mentioned in the first email
in the thread.

-- 
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com



Re: Code checks for App Devs, using new options for transaction behavior

From
Dilip Kumar
Date:
On Mon, Oct 31, 2022 at 4:23 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:
>
> On Sun, Oct 30, 2022 at 11:32 PM Simon Riggs
> <simon.riggs@enterprisedb.com> wrote:
> >
> > On Fri, 28 Oct 2022 at 10:33, Simon Riggs <simon.riggs@enterprisedb.com> wrote:
> >
> > > Thanks for the feedback, I will make all of those corrections in the
> > > next version.
> >
> > New version attached. I've rolled 002-004 into one patch, but can
> > split again as needed.
>
> I like the idea of "parse only" and "nested xact", thanks for working
> on this.  I will look into patches in more detail, especially nested
> xact. IMHO there is no point in merging "nested xact" and "rollback on
> commit". They might be changing the same code location but these two
> are completely different ideas, in fact all these three should be
> reviewed as three separate threads as you mentioned in the first email
> in the thread.

What is the behavior if "nested_transactions" value is changed within
a transaction execution, suppose the value was on and we have created
a few levels of nested subtransactions and within the same transaction
I switched it to off or to outer?

-- 
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com



Re: Code checks for App Devs, using new options for transaction behavior

From
Dilip Kumar
Date:
On Mon, Oct 31, 2022 at 5:03 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:
>
> On Mon, Oct 31, 2022 at 4:23 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:
> >
> > On Sun, Oct 30, 2022 at 11:32 PM Simon Riggs
> > <simon.riggs@enterprisedb.com> wrote:
> > >
> > > On Fri, 28 Oct 2022 at 10:33, Simon Riggs <simon.riggs@enterprisedb.com> wrote:
> > >
> > > > Thanks for the feedback, I will make all of those corrections in the
> > > > next version.
> > >
> > > New version attached. I've rolled 002-004 into one patch, but can
> > > split again as needed.
> >
> > I like the idea of "parse only" and "nested xact", thanks for working
> > on this.  I will look into patches in more detail, especially nested
> > xact. IMHO there is no point in merging "nested xact" and "rollback on
> > commit". They might be changing the same code location but these two
> > are completely different ideas, in fact all these three should be
> > reviewed as three separate threads as you mentioned in the first email
> > in the thread.
>
> What is the behavior if "nested_transactions" value is changed within
> a transaction execution, suppose the value was on and we have created
> a few levels of nested subtransactions and within the same transaction
> I switched it to off or to outer?

1.
@@ -3815,6 +3861,10 @@ PrepareTransactionBlock(const char *gid)
     /* Set up to commit the current transaction */
     result = EndTransactionBlock(false);

+    /* Don't allow prepare until we are back to an unnested state at level 0 */
+    if (XactNestingLevel > 0)
+        return false;


I am not sure whether it is good to not allow PREPARE or we can just
prepare it and come out of the complete nested transaction.  Suppose
we have multiple savepoints and we say prepare then it will just
succeed so why does it have to be different here?


2.        case TBLOCK_SUBABORT:
             ereport(WARNING,
                     (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
                      errmsg("there is already a transaction in progress")));
+            if (XactNesting == XACT_NEST_OUTER)
+                XactNestingLevel++;
             break;

I did not understand what this change is for, can you tell me the
scenario or a test case to hit this?

Remaining part w.r.t "nested xact" patch looks fine, I haven't tested
it yet though.

-- 
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com



Re: Code checks for App Devs, using new options for transaction behavior

From
Simon Riggs
Date:
On Mon, 31 Oct 2022 at 11:33, Dilip Kumar <dilipbalaut@gmail.com> wrote:

> What is the behavior if "nested_transactions" value is changed within
> a transaction execution, suppose the value was on and we have created
> a few levels of nested subtransactions and within the same transaction
> I switched it to off or to outer?

Patch does the same dance as with other xact variables.

XactNesting is the value within the transaction and in the patch this
is not exported, so cannot be set externally.

XactNesting is set at transaction start to the variable
DefaultXactNesting, which is set by the GUC.

So its not a problem, but thanks for checking.

-- 
Simon Riggs                http://www.EnterpriseDB.com/



Re: Code checks for App Devs, using new options for transaction behavior

From
Simon Riggs
Date:
On Mon, 31 Oct 2022 at 12:22, Dilip Kumar <dilipbalaut@gmail.com> wrote:
>
> On Mon, Oct 31, 2022 at 5:03 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:
> >
> > On Mon, Oct 31, 2022 at 4:23 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:
> > >
> > > On Sun, Oct 30, 2022 at 11:32 PM Simon Riggs
> > > <simon.riggs@enterprisedb.com> wrote:
> > > >
> > > > On Fri, 28 Oct 2022 at 10:33, Simon Riggs <simon.riggs@enterprisedb.com> wrote:
> > > >
> > > > > Thanks for the feedback, I will make all of those corrections in the
> > > > > next version.
> > > >
> > > > New version attached. I've rolled 002-004 into one patch, but can
> > > > split again as needed.
> > >
> > > I like the idea of "parse only" and "nested xact", thanks for working
> > > on this.  I will look into patches in more detail, especially nested
> > > xact. IMHO there is no point in merging "nested xact" and "rollback on
> > > commit". They might be changing the same code location but these two
> > > are completely different ideas, in fact all these three should be
> > > reviewed as three separate threads as you mentioned in the first email
> > > in the thread.
> >
> > What is the behavior if "nested_transactions" value is changed within
> > a transaction execution, suppose the value was on and we have created
> > a few levels of nested subtransactions and within the same transaction
> > I switched it to off or to outer?
>
> 1.
> @@ -3815,6 +3861,10 @@ PrepareTransactionBlock(const char *gid)
>      /* Set up to commit the current transaction */
>      result = EndTransactionBlock(false);
>
> +    /* Don't allow prepare until we are back to an unnested state at level 0 */
> +    if (XactNestingLevel > 0)
> +        return false;
>
>
> I am not sure whether it is good to not allow PREPARE or we can just
> prepare it and come out of the complete nested transaction.  Suppose
> we have multiple savepoints and we say prepare then it will just
> succeed so why does it have to be different here?

I'm happy to discuss what the behavior should be in this case. It is
not a common case,
and people don't put PREPARE in their scripts except maybe in a test.

My reasoning for this code is that we don't want to accept a COMMIT
until we reach top-level of nesting,
so the behavior should be similar for PREPARE, which is just
first-half of final commit.

Note that the nesting of begin/commit is completely separate to the
existence/non-existence of subtransactions, especially with
nested_transactions = 'outer'


> 2.        case TBLOCK_SUBABORT:
>              ereport(WARNING,
>                      (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
>                       errmsg("there is already a transaction in progress")));
> +            if (XactNesting == XACT_NEST_OUTER)
> +                XactNestingLevel++;
>              break;
>
> I did not understand what this change is for, can you tell me the
> scenario or a test case to hit this?

Well spotted, thanks. That seems to be some kind of artefact.

There is no test that exercises that since it is an unintended change,
so I have removed it.


> Remaining part w.r.t "nested xact" patch looks fine, I haven't tested
> it yet though.

New versions attached, separated again as you suggested.

-- 
Simon Riggs                http://www.EnterpriseDB.com/

Attachment

Re: Code checks for App Devs, using new options for transaction behavior

From
Dilip Kumar
Date:
On Mon, Oct 31, 2022 at 6:54 PM Simon Riggs
<simon.riggs@enterprisedb.com> wrote:
>
> > > What is the behavior if "nested_transactions" value is changed within
> > > a transaction execution, suppose the value was on and we have created
> > > a few levels of nested subtransactions and within the same transaction
> > > I switched it to off or to outer?

I think you missed the above comment?

> > I am not sure whether it is good to not allow PREPARE or we can just
> > prepare it and come out of the complete nested transaction.  Suppose
> > we have multiple savepoints and we say prepare then it will just
> > succeed so why does it have to be different here?
>
> I'm happy to discuss what the behavior should be in this case. It is
> not a common case,
> and people don't put PREPARE in their scripts except maybe in a test.
>
> My reasoning for this code is that we don't want to accept a COMMIT
> until we reach top-level of nesting,
> so the behavior should be similar for PREPARE, which is just
> first-half of final commit.

Yeah this is not a very common case.  And we can see opinions from
others as well.  But I think your reasoning for doing it this way also
makes sense to me.

I have some more comments for 0002
1.
+            if (XactNesting == XACT_NEST_OUTER && XactNestingLevel > 0)
+            {
+                /* Throw ERROR */
+                ereport(ERROR,
+                        (errmsg("nested ROLLBACK, level %u aborts
outer transaction", XactNestingLevel--)));
+            }

I did not understand in case of 'outer' if we are giving rollback from
inner nesting level why it is throwing error?  Documentation just says
this[1] but it did not
mention the error.  I agree that we might need to give the rollback as
many times as the nesting level but giving errors seems confusing to
me.

[1]
+       <para>
+        A setting of <quote>outer</quote> will cause a nested
+        <command>BEGIN</command> to be remembered, so that an equal number
+        of <command>COMMIT</command> or <command>ROLLBACK</command> commands
+        are required to end the nesting. In that case a
<command>ROLLBACK</command>
+        at any level will abort the entire outer transaction.
+        Once we reach the top-level transaction,
+        the final <command>COMMIT</command> will end the transaction.
+        This ensures that all commands within the outer transaction are atomic.
+       </para>


2.

+            if (XactNesting == XACT_NEST_OUTER)
+            {
+                if (XactNestingLevel <= 0)
+                    s->blockState = TBLOCK_END;
+                else
+                    ereport(NOTICE,
+                            (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
+                             errmsg("nested COMMIT, level %u",
XactNestingLevel)));
+                XactNestingLevel--;
+                return true;
+            }

+            while (s->parent != NULL && !found_subxact)
             {
+                if (XactNesting == XACT_NEST_ALL &&
+                    XactNestingLevel > 0 &&
+                    PointerIsValid(s->name) &&
+                    strcmp(s->name, NESTED_XACT_NAME) == 0)
+                    found_subxact = true;
+
                 if (s->blockState == TBLOCK_SUBINPROGRESS)
                     s->blockState = TBLOCK_SUBCOMMIT

I think these changes should be explained in the comments.

3.

+            if (XactNesting == XACT_NEST_OUTER)
+            {
+                if (XactNestingLevel > 0)
+                {
+                    ereport(NOTICE,
+                            (errmsg("nested COMMIT, level %u in
aborted transaction", XactNestingLevel)));
+                    XactNestingLevel--;
+                    return false;
+                }
+            }

Better to write this as if (XactNesting == XACT_NEST_OUTER &&
XactNestingLevel > 0) instead of two levels nested if conditions.

4.
+                if (XactNesting == XACT_NEST_ALL &&
+                    XactNestingLevel > 0 &&
+                    PointerIsValid(s->name) &&
+                    strcmp(s->name, NESTED_XACT_NAME) == 0)
+                    found_subxact = true;

I think this strcmp(s->name, NESTED_XACT_NAME) is done because there
could be other types of internal subtransaction also like savepoints?
What will be the behavior if someone declares a savepoint with this
name ("_internal_nested_xact").  Will this interfere with this new
functionality? Have we tested scenarios like that?

-- 
Regards,
Dilip Kumar
EnterpriseDB: http://www.enterprisedb.com



Re: Code checks for App Devs, using new options for transaction behavior

From
Simon Riggs
Date:
On Wed, 2 Nov 2022 at 03:52, Dilip Kumar <dilipbalaut@gmail.com> wrote:
>
> On Mon, Oct 31, 2022 at 6:54 PM Simon Riggs
> <simon.riggs@enterprisedb.com> wrote:
> >
> > > > What is the behavior if "nested_transactions" value is changed within
> > > > a transaction execution, suppose the value was on and we have created
> > > > a few levels of nested subtransactions and within the same transaction
> > > > I switched it to off or to outer?
>
> I think you missed the above comment?

[copy of earlier reply]

Patch does the same dance as with other xact variables.

XactNesting is the value within the transaction and in the patch this
is not exported, so cannot be set externally.

XactNesting is set at transaction start to the variable
DefaultXactNesting, which is set by the GUC.

So its not a problem, but thanks for checking.

> > > I am not sure whether it is good to not allow PREPARE or we can just
> > > prepare it and come out of the complete nested transaction.  Suppose
> > > we have multiple savepoints and we say prepare then it will just
> > > succeed so why does it have to be different here?
> >
> > I'm happy to discuss what the behavior should be in this case. It is
> > not a common case,
> > and people don't put PREPARE in their scripts except maybe in a test.
> >
> > My reasoning for this code is that we don't want to accept a COMMIT
> > until we reach top-level of nesting,
> > so the behavior should be similar for PREPARE, which is just
> > first-half of final commit.
>
> Yeah this is not a very common case.  And we can see opinions from
> others as well.  But I think your reasoning for doing it this way also
> makes sense to me.
>
> I have some more comments for 0002
> 1.
> +            if (XactNesting == XACT_NEST_OUTER && XactNestingLevel > 0)
> +            {
> +                /* Throw ERROR */
> +                ereport(ERROR,
> +                        (errmsg("nested ROLLBACK, level %u aborts
> outer transaction", XactNestingLevel--)));
> +            }
>
> I did not understand in case of 'outer' if we are giving rollback from
> inner nesting level why it is throwing error?  Documentation just says
> this[1] but it did not
> mention the error.  I agree that we might need to give the rollback as
> many times as the nesting level but giving errors seems confusing to
> me.

Docs mention ROLLBACK at any level will abort the transaction, which
is what the ERROR does.

> [1]
> +       <para>
> +        A setting of <quote>outer</quote> will cause a nested
> +        <command>BEGIN</command> to be remembered, so that an equal number
> +        of <command>COMMIT</command> or <command>ROLLBACK</command> commands
> +        are required to end the nesting. In that case a
> <command>ROLLBACK</command>
> +        at any level will abort the entire outer transaction.
> +        Once we reach the top-level transaction,
> +        the final <command>COMMIT</command> will end the transaction.
> +        This ensures that all commands within the outer transaction are atomic.
> +       </para>
>
>
> 2.
>
> +            if (XactNesting == XACT_NEST_OUTER)
> +            {
> +                if (XactNestingLevel <= 0)
> +                    s->blockState = TBLOCK_END;
> +                else
> +                    ereport(NOTICE,
> +                            (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
> +                             errmsg("nested COMMIT, level %u",
> XactNestingLevel)));
> +                XactNestingLevel--;
> +                return true;
> +            }

This is decrementing the nesting level for XACT_NEST_OUTER,
until we reach the top level, when the commit is allowed.

> +            while (s->parent != NULL && !found_subxact)
>              {
> +                if (XactNesting == XACT_NEST_ALL &&
> +                    XactNestingLevel > 0 &&
> +                    PointerIsValid(s->name) &&
> +                    strcmp(s->name, NESTED_XACT_NAME) == 0)
> +                    found_subxact = true;
> +
>                  if (s->blockState == TBLOCK_SUBINPROGRESS)
>                      s->blockState = TBLOCK_SUBCOMMIT
>
> I think these changes should be explained in the comments.

This locates the correct subxact by name, as you mention in (4)

> 3.
>
> +            if (XactNesting == XACT_NEST_OUTER)
> +            {
> +                if (XactNestingLevel > 0)
> +                {
> +                    ereport(NOTICE,
> +                            (errmsg("nested COMMIT, level %u in
> aborted transaction", XactNestingLevel)));
> +                    XactNestingLevel--;
> +                    return false;
> +                }
> +            }
>
> Better to write this as if (XactNesting == XACT_NEST_OUTER &&
> XactNestingLevel > 0) instead of two levels nested if conditions.

Sure. I had been aiming for clarity.

> 4.
> +                if (XactNesting == XACT_NEST_ALL &&
> +                    XactNestingLevel > 0 &&
> +                    PointerIsValid(s->name) &&
> +                    strcmp(s->name, NESTED_XACT_NAME) == 0)
> +                    found_subxact = true;
>
> I think this strcmp(s->name, NESTED_XACT_NAME) is done because there
> could be other types of internal subtransaction also like savepoints?

In XACT_NEST_ALL mode, each nested subxact that is created needs a name.
The name is used to ensure we roll back to the correct subxact, which
might exist.

> What will be the behavior if someone declares a savepoint with this
> name ("_internal_nested_xact").  Will this interfere with this new
> functionality?

Clearly! I guess you are saying we should disallow them.

> Have we tested scenarios like that?

No, but that can be done.

-- 
Simon Riggs                http://www.EnterpriseDB.com/



Re: Code checks for App Devs, using new options for transaction behavior

From
Simon Riggs
Date:
On Wed, 2 Nov 2022 at 07:40, Simon Riggs <simon.riggs@enterprisedb.com> wrote:

> > What will be the behavior if someone declares a savepoint with this
> > name ("_internal_nested_xact").  Will this interfere with this new
> > functionality?
>
> Clearly! I guess you are saying we should disallow them.
>
> > Have we tested scenarios like that?
>
> No, but that can be done.

More tests as requested, plus minor code rework, plus comment updates.

-- 
Simon Riggs                http://www.EnterpriseDB.com/

Attachment

Re: Code checks for App Devs, using new options for transaction behavior

From
Simon Riggs
Date:
On Mon, 7 Nov 2022 at 14:25, Simon Riggs <simon.riggs@enterprisedb.com> wrote:
>
> On Wed, 2 Nov 2022 at 07:40, Simon Riggs <simon.riggs@enterprisedb.com> wrote:
>
> > > What will be the behavior if someone declares a savepoint with this
> > > name ("_internal_nested_xact").  Will this interfere with this new
> > > functionality?
> >
> > Clearly! I guess you are saying we should disallow them.
> >
> > > Have we tested scenarios like that?
> >
> > No, but that can be done.
>
> More tests as requested, plus minor code rework, plus comment updates.

New versions

-- 
Simon Riggs                http://www.EnterpriseDB.com/

Attachment

Re: Code checks for App Devs, using new options for transaction behavior

From
Greg Stark
Date:
On Thu, 27 Oct 2022 at 07:10, Simon Riggs <simon.riggs@enterprisedb.com> wrote:
>
> In the past, developers have wondered how we can provide "--dry-run"
> functionality

That would be an awesome functionality, indeed. I have concerns of how
feasible it is in general but I think providing features to allow
developers to build it for their use cases is a good approach. The
corner cases that might not be possible in general might be tractable
developers willing to constrain their development environment or use
information available outside Postgres.

But... I have concerns about some of the design here.

> * psql --parse-only
> Checks the syntax of all SQL in a script, but without actually
> executing it. This is very important in the early stages of complex
> migrations because we need to see if the code would generate syntax
> errors before we attempt to execute it. When there are many
> dependencies between objects, actual execution fails very quickly if
> we run in a single transaction, yet running outside of a transaction
> can leave a difficult cleanup task. Fixing errors iteratively is
> difficult when there are long chains of dependencies between objects,
> since there is no easy way to predict how long it will take to make
> everything work unless you understand how many syntax errors exist in
> the script.
> 001_psql_parse_only.v1.patch

This effectively enables \gdesc mode for every query. It needs docs
explaining what's actually going to happen and how to use it because
that wasn't super obvious to me even after reading the patch. I'm not
sure reusing DescribeQuery() and then returning early is the best
idea.

But more importantly it's only going to handle the simplest scripts
that don't do DDL that further statements will depend on. That at
least needs to be documented.


> * nested transactions = off (default) | all | on
> Handle nested BEGIN/COMMIT, which can cause chaos on failure. This is
> an important part of guaranteeing that everything that gets executed
> is part of a single atomic transaction, which can then be rolled back
> - this is a pre-requisite for the last feature.
> 002_nested_xacts.v7.patch
> The default behavior is unchanged (off)
> Setting "all" treats nested BEGIN/COMMIT as subtransactions, allowing
> some parts to fail without rolling back the outer transaction.
> Setting "outer" flattens nested BEGIN/COMMIT into one single outer
> transaction, so that any failure rolls back the entire transaction.

I think we've been burned pretty badly by GUCs that control SQL
semantics before. I think there was discussion at the time nested
transactions went in and there must have been a reason we did
SAVEPOINT rather than make nested BEGINs do things like this. But
regardless if we do want to change what nested BEGINs do I think we
have to decide what behaviour we want, think about the backwards
compatibility impacts, and make the change. We can't make it just for
some people some of the time based on a GUC. Doing that makes it
impossible to write scripts that work consistently.

I'm not clear what happens if you have this feature enabled and *also*
use SAVEPOINTs...

You say this is a prerequisite for 003 and I see how they're related
though I don't immediately see why it should be necessary to change
nested BEGIN behaviour to make that work.

> * rollback_on_commit = off (default) | on
> Force transactions to fail their final commit, ensuring that no
> lasting change is made when a script is tested. i.e. accept COMMIT,
> but do rollback instead.
> 003_rollback_on_commit.v1.patch

I suppose technically this is also a "semantics controlled by a GUC"
but I guess it's safe since you would only set this when you want this
debugging environment and then you really do want it to be global.

I'm not sure it's super safe though. Like, dblink connections can
break it, what happens if it gets turned off midway through a
transaction?

I wonder if this should be handled by the client itself the way
autocommit is. Like have an "autocommit" mode of "autorollback"
instead. That would mean having to add it to every client library of
course but then perhaps it would be able to change client behaviour in
ways that make sense at the same time.


-- 
greg



Greg Stark <stark@mit.edu> writes:
> On Thu, 27 Oct 2022 at 07:10, Simon Riggs <simon.riggs@enterprisedb.com> wrote:
>> * nested transactions = off (default) | all | on
>> Handle nested BEGIN/COMMIT, which can cause chaos on failure.

> I think we've been burned pretty badly by GUCs that control SQL
> semantics before.

Yeah, this idea is an absolute nonstarter.  rollback_on_commit seems
excessively dangerous as well compared to the value.

> I think there was discussion at the time nested
> transactions went in and there must have been a reason we did
> SAVEPOINT rather than make nested BEGINs do things like this.

I believe the reason was "because the SQL standard says so".

I'm not sure if any of these proposals are still live now that
Simon's retired.  Presumably somebody else would have to push
them forward for there to be a chance of anything happening.

            regards, tom lane