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 1mX4Sw-0005Af-HZ for psycopg@arkaria.postgresql.org; Sun, 03 Oct 2021 16:35:54 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1mX4Su-00085l-A5 for psycopg@arkaria.postgresql.org; Sun, 03 Oct 2021 16:35:52 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mX4Su-00085d-06 for psycopg@lists.postgresql.org; Sun, 03 Oct 2021 16:35:52 +0000 Received: from cross.elm.relay.mailchannels.net ([23.83.212.46]) by makus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mX4Sq-0004Zs-MX for psycopg@postgresql.org; Sun, 03 Oct 2021 16:35:50 +0000 X-Sender-Id: cp7oy65bvo|x-authuser|paolo@paolodestefani.it Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 4C1E53417C9; Sun, 3 Oct 2021 16:35:46 +0000 (UTC) Received: from lu-shared04.cpanelplatform.com (100-96-16-65.trex.outbound.svc.cluster.local [100.96.16.65]) (Authenticated sender: cp7oy65bvo) by relay.mailchannels.net (Postfix) with ESMTPA id 1B09B3419E2; Sun, 3 Oct 2021 16:35:44 +0000 (UTC) X-Sender-Id: cp7oy65bvo|x-authuser|paolo@paolodestefani.it Received: from lu-shared04.cpanelplatform.com ([UNAVAILABLE]. [107.189.4.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384) by 100.96.16.65 (trex/6.4.3); Sun, 03 Oct 2021 16:35:46 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: cp7oy65bvo|x-authuser|paolo@paolodestefani.it X-MailChannels-Auth-Id: cp7oy65bvo X-Name-Squirrel: 2ef5f1f242186ea0_1633278946071_3707996835 X-MC-Loop-Signature: 1633278946071:1112253319 X-MC-Ingress-Time: 1633278946071 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=paolodestefani.it; s=default; h=Content-Transfer-Encoding:Content-Type: Message-ID:References:In-Reply-To:Subject:Cc:To:From:Date:MIME-Version:Sender :Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=azvL/5RrF1DPp0ncuqwqJTmSk3jFb4aVUikXz7+a38c=; b=EV1VzoZKEJ1yJxqLRwnH6hCk5P 3NZvY+ozFV9b1/wlMa8M72a9h3UqYn2UQl8SBXsyf3WE5k0OjM3dUpsSzdcw03BdeIpNI/Im8Y9sl jWMnFg6CnXkPjwjgmXOUoTo80J0pTmLPJYtEI64nTQWl2P7/xKpMnJg1rfhqthorVuGQTYmS9vHWe /4iEHr7XIqW57UE2C0eqbfAOLWRW6QbJwhHPu3NRI5mG1X9du1SsPzs0HjCBxL3aOeZmXmh6+Q/5z Dg4hXKadAPcnw2+LpGHnK9hqXArnGQhDVCesSG/Y8A3EX+dIXcNT059+Q/WQO0167HWeT8ttO0NUC sZAgltFg==; Received: from [::1] (port=42364 helo=lu-shared04.cpanelplatform.com) by lu-shared04.cpanelplatform.com with esmtpa (Exim 4.94.2) (envelope-from ) id 1mX4Sj-00FpuE-Ks; Sun, 03 Oct 2021 18:35:41 +0200 MIME-Version: 1.0 Date: Sun, 03 Oct 2021 18:35:41 +0200 From: Paolo De Stefani To: Daniele Varrazzo Cc: psycopg@postgresql.org Subject: Re: psycopg3 and cur.description behavior In-Reply-To: References: User-Agent: Roundcube Webmail/1.4.11 Message-ID: X-Sender: paolo@paolodestefani.it Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-AuthUser: paolo@paolodestefani.it List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Il 03/10/2021 17:33 Daniele Varrazzo ha scritto: > On Sun, 3 Oct 2021 at 15:33, Paolo De Stefani > wrote: >> >> Hello psycopg users >> This is my first post on this mailing list > > Hello Paolo, welcome here. > >> In my python code i use the return query to create a dictionary in > this >> way: >> >> session.update(dict(zip([i[0] for i in cur.description], >> cur.fetchall()[0]))) >> >> This part no longer works in psycopg 3. Looks like cur.description > in >> psycopg 3 is different if i execute a query or call a function. > Are you sure you are using the same statements in psycopg 2 and 3? Of course in psycopg2 i use: cur.callproc('system.pa_connect', (MRV_PGSQL, APPNAME, APPVERSION, par['user'], par['password'], par['hostname'])) > If > you call `select pa_connect` or `select * from pa_connect` you get > different results: a table with a single column of records in the > first case, expanded records in the second. You can verify that in > psql too. Using a simplified set returning function: > > piro=# create or replace function testfunc() returns table(pid int, > type text) language plpgsql as $$ > begin > return query select a.pid, a.backend_type from pg_stat_activity a; > end$$; > > piro=# select * from testfunc() limit 3; > ┌────────┬──────────────────────────────┐ > │ pid │ type │ > ├────────┼──────────────────────────────┤ > │ 625446 │ autovacuum launcher │ > │ 625448 │ logical replication launcher │ > │ 806502 │ client backend │ > └────────┴──────────────────────────────┘ > (3 rows) > > piro=# select testfunc() limit 3; > ┌─────────────────────────────────────────┐ > │ testfunc │ > ├─────────────────────────────────────────┤ > │ (625446,"autovacuum launcher") │ > │ (625448,"logical replication launcher") │ > │ (806502,"client backend") │ > └─────────────────────────────────────────┘ > (3 rows) > > Psycopg would see pretty much the same: in psycopg2 you obtain two > columns if you use "select * from", only one "record" column if you > don't: > > In [1]: import psycopg2 > In [2]: cnn = psycopg2.connect("") > In [3]: cur = cnn.cursor() > > In [4]: cur.execute("select * from testfunc()") > In [5]: cur.description > Out[5]: (Column(name='pid', type_code=23), Column(name='type', > type_code=25)) > In [6]: cur.fetchone() > Out[6]: (625446, 'autovacuum launcher') > > In [7]: cur.execute("select testfunc()") > In [8]: cur.description > Out[8]: (Column(name='testfunc', type_code=2249),) > In [9]: cur.fetchone() > Out[9]: ('(625446,"autovacuum launcher")',) > > Psycopg 3 returns something similar: > > In [1]: import psycopg > In [2]: cnn = psycopg.connect("") > > In [3]: cur = cnn.execute("select * from testfunc()") > In [4]: cur.description > Out[4]: [, text (oid: 25)>] > In [5]: cur.fetchone() > Out[5]: (625446, 'autovacuum launcher') > > In [6]: cur = cnn.execute("select testfunc()") > In [7]: cur.description > Out[7]: [] > In [8]: cur.fetchone() > Out[8]: (('625446', 'autovacuum launcher'),) > > There is a difference in how the record is handled: psycopg 2 doesn't > parse it, psycopg 3 unpacks it in a tuple (although it doesn't have > enough info to understand the types contained in the record, so they > are left as strings). However the number and oids of the columns in > the result is the same. The pattern you use to convert the record into > a dict should work the same way in psycopg 3 too: > > In [9]: cur = cnn.execute("select * from testfunc()") > > In [10]: dict(zip([i[0] for i in cur.description],cur.fetchall()[0])) > Out[10]: {'pid': 625446, 'type': 'autovacuum launcher'} > >> BUT if i execute (call) the already mentioned postgresql function: >> >> cur.execute('SELECT system.pa_connect(%s, %s, %s, %s, %s, %s);', >> ... > > I think you want to use `SELECT * FROM system.pa_connect(...)` here, > and I think it is what you were using before. > > Does it make sense? I see thanks for the clear explanation > > Once you are comfortable with how the types of query work, you might > want to take a look at 'dict_row()' > (https://www.psycopg.org/psycopg3/docs/advanced/rows.html) to convert > records to dicts in a more succinct way: > > In [11]: from psycopg.rows import dict_row > > In [12]: cur = cnn.cursor(row_factory=dict_row) > > In [13]: cur.execute("select * from testfunc()").fetchone() > Out[13]: {'pid': 625446, 'type': 'autovacuum launcher'} Yes, that's what i will use, thanks By the way i didn't see any cur.mogrify() in psycopg 3 and no logging cursor as well something i used frequently Are they no more available ? Any plan to include them in next versions ? > > Cheers > > -- Daniele -- Paolo De Stefani