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 1kkQBc-0006uy-KU for psycopg@arkaria.postgresql.org; Wed, 02 Dec 2020 11:20:40 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1kkQBZ-00009K-JZ for psycopg@arkaria.postgresql.org; Wed, 02 Dec 2020 11:20:37 +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 1kkQBZ-00009D-CB for psycopg@lists.postgresql.org; Wed, 02 Dec 2020 11:20:37 +0000 Received: from mail-lf1-x12f.google.com ([2a00:1450:4864:20::12f]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1kkQBR-0001TY-QO for psycopg@postgresql.org; Wed, 02 Dec 2020 11:20:37 +0000 Received: by mail-lf1-x12f.google.com with SMTP id t6so3949620lfl.13 for ; Wed, 02 Dec 2020 03:20:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:from:date:message-id:subject:to; bh=pWQwgf69ZGEAk6AfsHYG4vhad5S8pTYGqE5vp9680IE=; b=nJM+e1Wv0QXaz5ZjuwFcNbxs7cZBj/7eIkw8QviHB5UcAVdusXk4thZ7qff6DcG5N6 DzJwoLN14kIAa5lsUsFcAqrfjPfLD+tQmBIpq//dVp3p03LKEgQwtKFmwGgvLvjNehkv tt+QaJfS3wnc6DdAsEa00JenVVwNMrhdskhwFKmyIbiRTSl3tkD1nA4pS3ljAI8+fKs7 eQHcq6eKbQ8JU49u0fvS/CUJ7vKWvWrsc8okZACubxnpeWytr0pO5RHdxadAJWnvpDqU a6BNJkHUk8xJBCa8DsnXgpgwWLc/EMPrDM0vjHFSpuByoEsi7BrO5JP5CtrDADacgZbT J+Yw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=pWQwgf69ZGEAk6AfsHYG4vhad5S8pTYGqE5vp9680IE=; b=rVz3x3++v2unL+zBIvs3yN4jfm9kjP52KoWP4Rs6ig4JnP1Ag/bfJahRgljw5zUMr7 WLpKmR9wEkf9M5b8BEG0zziIkoYNMU2cF117MHtZbv6LpJBwMk8DZ36mXm0rv1SeEn/4 D1jCXgfr+h0XU8PrkE7APemH0g+SR8OPaiAJQgC2TLR2ObwxFzL1v0xs6IaXQEYZizW8 Yimjgfo5KhVG/doMSUH7Y90NmAE7HxSQAKxR2/t49engPVrNjLB5uZdBhov8gsppBLeZ GBsi5LtCq430Au+V34PkKnTSmcBXk4tzIDqhX6ZurGGsasms3sp2FWhzQoXLgQXXkfK+ GB/g== X-Gm-Message-State: AOAM532kLPnVmkhRel3Bbco8x2VhGWOfD/TEIaly83q7XW/HwgyEYOxO EHYc7sBIQ0MM5yHMP68uXMVuKgYJ1DQ2G4VyrTmSXlSR9vQ= X-Google-Smtp-Source: ABdhPJy+kHBY5UgfzXiybzYCRn390O2u+RLbVGjaYBsAcD2heSSaMeR27tYpcGGrwLsrkbq6usD5nn3CPy/EFl3Rhpo= X-Received: by 2002:ac2:46ef:: with SMTP id q15mr993658lfo.2.1606908028169; Wed, 02 Dec 2020 03:20:28 -0800 (PST) MIME-Version: 1.0 From: Daniele Varrazzo Date: Wed, 2 Dec 2020 11:20:16 +0000 Message-ID: Subject: Executing on the connection? To: psycopg@postgresql.org Content-Type: text/plain; charset="UTF-8" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk Hello, if there is a thing that people approaching psycopg find confusing is the connection/cursor duality. The connection object as a wrapper for the database connection is clear to understand for anyone who has used psql. But the cursor as a means to give command is spurious: you can give commands but all the cursors are serialised, not parallelised, on the same connection, and it isn't useful for transactions either... The only unique thing a cursor has is to hold a result and consume it; for the rest it doesn't own neither the postgres session nor the transaction. But it gives them command. Weird. You are surely familiar with the psycopg2 usage pattern: conn = psycopg2.connect(dsn) cur = conn.cursor() cur.execute(query, params): for record in cur: ... # do something The cursor() can take parameters, e.g. to create server-side "named" cursors, but most often people will use the standard client-side cursor, which is a lightweight object, little more than a wrapper for a PGresult. One little change I've made to psycopg3 cursors is to make it return "self" on execute() (it currently returns None, so it's totally unused). This allows chaining a fetch operation right after execute, so the pattern above can be reduced to: conn = psycopg3.connect(dsn) cur = conn.cursor() record = cur.execute(query, params).fetchone() # or for record in cur.execute(query, params): ... # do something And of course nastymess such as: conn.cursor().execute(query).fetchone() psycopg3.connect(dsn).cursor().execute(query).fetchone() But, taste. I'm toying with the idea of adding a 'connection.execute(query, [params])' methd, which would basically just create a cursor internally, query on it, and return it. No parameter could be passed to the cursor() call, so it could only create the most standard, client-side cursor (or whatever the default for the connection is, if there is some form of cursor_factory, which hasn't been implemented in psycopg3 yet). For anything more fancy, cursor() should be called explicitly. As a result people could use: conn = psycopg3.connect(dsn) record = conn.execute(query, params).fetchone() # or for record in conn.execute(query, params): ... # do something No other methods bloating the connection interface: no executemany(), copy(), callproc (actually there will be no callproc at all in psycopg3: postgres has no fast path for function call and too much semantics around stored procedure that a single callproc() couldn't cover). Being the cursor client-side, its close() doesn't actually do anythin apart from making it unusable, so just disposing of it without calling close() is totally safe. Thoughts? Cheers! -- Daniele