Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1macm9-0005OR-8S for psycopg@arkaria.postgresql.org; Wed, 13 Oct 2021 11:50:25 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1macm6-0004jD-LE for psycopg@arkaria.postgresql.org; Wed, 13 Oct 2021 11:50:22 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1macm6-0004j5-Dz for psycopg@lists.postgresql.org; Wed, 13 Oct 2021 11:50:22 +0000 Received: from mail-ua1-x92f.google.com ([2607:f8b0:4864:20::92f]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1macm3-0006yd-VT for psycopg@postgresql.org; Wed, 13 Oct 2021 11:50:21 +0000 Received: by mail-ua1-x92f.google.com with SMTP id h19so3847183uax.5 for ; Wed, 13 Oct 2021 04:50:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=oiiZoMPbQ1LIzOL9XAj+VJDQ0MuHjlMUIBegA74EGdo=; b=Ux1sgmsxD2AusZFHNt5NdI/08Kw3KqC4ZkK4aRmvCxpSm9zKoHjS1m6dfclC83kSMi sHkv3qUv4DY1aYRiTwzpOxbA6IeEx2NabH7JprVHi//fmA1Wp5k6+gg2iv5DahNMh5T9 UXOH69oh38GmJRwvzlS42ye4Uhi3gxFy7McnRzvZptI/D29i/Yi6/nEV7nrl6JO20j6I /Vz/92Muig65IW2FBNkTJVLCK0InW62MPF8FavDy4cKOerg2DeyDCK3//YqWzkMpN1Vq IlZZk8yreU6WHe/twc/Ainkmubo8Ti+MXaclDASKcz87QlQKYAbYsZwIq3bJNYDmlqy+ LJgw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=oiiZoMPbQ1LIzOL9XAj+VJDQ0MuHjlMUIBegA74EGdo=; b=3jBbrp+3sDXdzZsMLv+7uNki3Zyesa6lSTuvaGRqtKDz/WkD6WrNI+QtiRBqy+PfZo fGIuXxkSm6KpeJFliJTG4aQiUhPx4KKk4N67mt596phEKsV3IDdHFsh+U22NxRFrH6NG Q254opa8aYgDGxagTjwWUZiaA9Vx+ZG57/jsG1eofSwqcRvJpK28UnxvZGovhRgd5VMe af92S4xemb46iLE7KCcvj/vVQtrBGDOplxjWjd95JNoPeNUcjGO//Xx2EQPiK3fP/HV+ Ss5M0ZYFXU9w0uCMqNIjFzvqpogXIO409hwlUWuyfaLlwXpm9PYi4ayAZG+m468yjldz ytHQ== X-Gm-Message-State: AOAM531EpSZfGO1LXymF6tQQDyMQzWMjZI5/inJWRkV4kDFwcr3aXNII +daKJAFSOZlSEOXEiEp6gEpNB8Ywvz/FkAbzykU= X-Google-Smtp-Source: ABdhPJyQK3sDsF+MSVpPn9ARlsTivDPzN/qH6lNIwj1Cx14m+dgJQFGI5QGj1JnO5SMSXy90EdqSTT4LmDTqxyr5Em0= X-Received: by 2002:a67:ef44:: with SMTP id k4mr37777524vsr.36.1634125817431; Wed, 13 Oct 2021 04:50:17 -0700 (PDT) MIME-Version: 1.0 References: <9a99d2d110b121542d1ea0a05066858b@paolodestefani.it> In-Reply-To: <9a99d2d110b121542d1ea0a05066858b@paolodestefani.it> From: Daniele Varrazzo Date: Wed, 13 Oct 2021 13:50:05 +0200 Message-ID: Subject: Re: psycopg3 transactions To: Paolo De Stefani Cc: Psycopg Content-Type: text/plain; charset="UTF-8" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Wed, 13 Oct 2021 at 13:09, Paolo De Stefani wrote:> > Well, everything is clear.. > > BUT > > I don't understand why a new transaction (committed while in the 'with' > context) is not commited (whitout any message) because a previous select > statement is not commited. If you are in a transaction, then `conn.transaction()` doesn't start a new transaction (implying that it commits the previous one). It will start a sub-transaction. > For example: > ... > >>> with con.cursor() as cur: > ... cur.execute('select code, description from system.app_user;') > ... > user=pa_login_role database=test) at 0x35b3f40> As you can see from the cursor repr, here you have already started a transaction, as per auto-transaction DBAPI default behaviour. This type of transaction requires you to `commit()` at the end. > IN PSQL: > > test=# select code, description from system.app_user; > code | description > --------+------------- > system | test > utente | ABCD > (2 righe) So far so good > IN PYTHON: > >>> with con.transaction(): > ... with con.cursor() as cur: > ... cur.execute("update system.app_user set description = > '1234' where code = 'utente';") > ... > user=pa_login_role database=test) at 0x35ec360> This didn't affect the previous transaction. It created a sub-transaction with a savepoint and only discarded that savepoint. The state of the connection is as it was before the block, i.e. in a DBAPI transaction. > >>> with con.cursor() as cur: > ... cur.execute('select code, description from system.app_user;') > ... > user=pa_login_role database=test) at 0x35b3f40> > >>> for i in cur: > ... print(i) > ... > ('system', 'test') > ('utente', '1234') This session is still in the middle of that active transaction, so it can see the data it changed. > IN PSQL: > test=# select code, description from system.app_user; > code | description > --------+------------- > system | test > utente | ABCD > (2 righe) But the transaction has not been committed, so psql can't see it yet. > So nothing changed for the psql connection even if a transaction was > commited (i think...) > if i close the connection > > IN PYTHON: > >>> con.close() You closed without the commit, so the transaction was discarded. > I tought that the "with con.transaction():" start a new transaction that > will be commeted anyway. > I have to do a con.commit() BEFORE the with con.transaction() block > instead. Or i have to use a transaction for every sql statement that is > what i'm doing now. > So i don't know if this is the normal/correct behaviour but it's very > easy to get wrong and get unexpected results The problem you are seeing comes from the DBAPI transaction that started automatically because you run a command outside a transaction() block. It has been confusing so far, with people classically expecting to see changes when the connection is closed, while the changes are discarded because no commit() has been executed. Your sequence of statements resulted in the execution of: > >>> with con.cursor() as cur: > ... cur.execute('select code, description from system.app_user;') BEGIN; SELECT ... > >>> with con.transaction(): > ... with con.cursor() as cur: > ... cur.execute("update system.app_user set description = > '1234' where code = 'utente';") SAVEPOINT x UPDATE ... RELEASE SAVEPOINT x > >>> with con.cursor() as cur: > ... cur.execute('select code, description from system.app_user;') SELECT > >>> con.close() CIAO - no COMMIT so everything was discarded Note: If you run with `conn.transaction()` and no other command had run before (i.e. the connection is IDLE, not INTRANS), then conn.transaction() does indeed start a new transaction, with BEGIN, but it will also run the COMMIT at the end of the block you would expect, leaving the connection back in IDLE state. But because of the SELECT at the beginning things go differently. My understanding is that the problem comes from the BEGIN run with your first select. I think, in order to make psycopg behave the way you want, you should have your connection in autocommit. This would have resulted in: > >>> with con.cursor() as cur: > ... cur.execute('select code, description from system.app_user;') SELECT ... > >>> with con.transaction(): > ... with con.cursor() as cur: > ... cur.execute("update system.app_user set description = > '1234' where code = 'utente';") BEGIN UPDATE ... COMMIT > >>> with con.cursor() as cur: > ... cur.execute('select code, description from system.app_user;') SELECT > >>> con.close() CIAO - but this time the data is safe. I think we can improve the documentation there by extending more about the effects of the interaction between DBAPI transactions and the transaction() blocks. And things are definitely more intuitive if transaction() is used in autocommit: we might want to advise people to do that. Suggestions are welcome. -- Daniele