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 1krM7N-0000A0-P9 for psycopg@arkaria.postgresql.org; Mon, 21 Dec 2020 14:24:57 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1krM7M-0002nJ-Mm for psycopg@arkaria.postgresql.org; Mon, 21 Dec 2020 14:24:56 +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 1krM7M-0002mZ-F6 for psycopg@lists.postgresql.org; Mon, 21 Dec 2020 14:24:56 +0000 Received: from mail-lf1-x133.google.com ([2a00:1450:4864:20::133]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1krM7F-0007QL-JK for psycopg@postgresql.org; Mon, 21 Dec 2020 14:24:55 +0000 Received: by mail-lf1-x133.google.com with SMTP id h205so24078357lfd.5 for ; Mon, 21 Dec 2020 06:24:49 -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=aZjQltX2Yg4vyHsNkXymVm4nDWL1JAZ73f+ilhykvOQ=; b=Z5GdGsylR4E9Dq6plkksBRnkWJqnhGlz2drnqA3Uqh8mYGigCaFV6ceR9FGNA+KDQk uSSB6L4/rZCd6sz8RWdiQalbV98y2BDKVHRTZVoCZi+/ENTzTIOGDfztEZTkWWR+1O68 i1eVwB4G9CXmI8PGWwqMb5GAi88F2ioQIsnyCQtY9UwSPu/LuRv9nVNJumIaJd8MXSJO S9SyGlNhjivuQdZKk/YoBDm3usNPoyr/FMnp6S/ER9gpeEuEFEaxb3OHnCBJFFugwSJa 3OSKkLiVHB1YITCHq4zDgYYUcL62i4QlF5W+nlM45isap7/QDeLGcS69oX5pZAglpdgM uKKw== 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=aZjQltX2Yg4vyHsNkXymVm4nDWL1JAZ73f+ilhykvOQ=; b=GuJmgdlyKUD43URGFJSFEhzPz6/aFLtewrc6IZ1aVdcoZ/OFvK4t/wrL67Y1Swv15M uz+NCWzXLzHnXoRGLv80ts0A6d/eZaO6HFVJuQYnpRPper6OhNWKOzD/Yzp3tTwimptq 5VxEtziQD3+uGlfqod/DOamvLiSonlt+Fczq9GEzda0WlGIAUaihGvKnXC7f5054YOaw y/HynKro6obrQt/tVzBZ2XBYB5qfPL8JF1fY+rGshOCsmXUw0UV68BXkpq/JpPhxXoql KgoboWa+h6kH2+W4XcN/liO08m/CrWEs/9q4/ISuDv3tGSfwDjsSmsbEZK8CA0iczvLy WxLA== X-Gm-Message-State: AOAM531388iXTEJOQ7UkltXcOpBYwZoGskhor7d0rZQbFwRlrYVe8z9P Xv5hmq/K5HwKgHDmkssEM3tIXzNZKxSOS6m3YQYgHVtjar4CeA== X-Google-Smtp-Source: ABdhPJwmXJo9yS5+4RQ69wl3u2wLYp5POCcy589P9OdT0OtZGMgGrC8ztA+r/v6bm44pn3fLM+W9UMulUdFa3NKPp0w= X-Received: by 2002:a2e:810c:: with SMTP id d12mr7283978ljg.400.1608560688590; Mon, 21 Dec 2020 06:24:48 -0800 (PST) MIME-Version: 1.0 From: Daniele Varrazzo Date: Mon, 21 Dec 2020 14:24:37 +0000 Message-ID: Subject: psycopg3, prepared statements 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 The one thing, the most requested thing in psycopg, is support for prepared statements. In psycopg3 for the moment there is: - very low level support for prepared statement, i.e. wrapping of libpq functions such as PQsendPrepare/PQsendQueryPrepared (https://www.postgresql.org/docs/current/libpq-async.html#LIBPQ-PQSENDPREPARE) - automatic use of prepared statements in `cursor.executemany()`, which might eventually stop sucking. Gathering some ideas: Prepared statements in the server are per session, so any form of cache is better connected to the connection than the cursor, although the cursors are the obvious interface to give commands. In the past [1] I thought about exposing explicit prepare/deallocate on the cursor, and it was a single prepared query per cursor. A `cursor.prepare(query)` with no args doesn't have types information though: if any it should take an optional array of parameters to get types from. What I'm thinking about is to prepare queries automatically with a schema such: - decisions are made after the query is transformed to postgres format (i.e. it is reduced to bytes, all the client-side manipulations have been done, placeholders have been transformed to $ format). There is an object in psycopg3 that takes care of this transformation [2] - the number of times a query is seen is stored in a LRU cache on the connection - if a query is seen more than `connection.prepare_threshold` times (proposed default: 5) then it is prepared with the name f'pg3_{hash(query)}' and the following executions are prepared. - if more than `connection.prepared_number` queries are prepared, the one used least recently is deallocated and evicted from the cache (proposed default: 100). - Parameters may be fudged on the connection: prepared_threshold=0 would prepare all queries, prepared_threshold=None would disable preparing. - For the control freak, cursor.execute(query, params, prepare=True) would prepare the query immediately, if it isn't already, prepare=False would avoid preparation. The default None would enable the automatic choice. [1] https://gist.github.com/dvarrazzo/3797445 [2] https://github.com/psycopg/psycopg3/blob/c790a832/psycopg3/psycopg3/_queries.py#L27 What do you think? Cheers -- Daniele