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 1kbt4a-0006sY-Jo for psycopg@arkaria.postgresql.org; Sun, 08 Nov 2020 22:22:08 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1kbt4Z-0004zr-J6 for psycopg@arkaria.postgresql.org; Sun, 08 Nov 2020 22:22:07 +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 1kbt4Z-0004zk-BA for psycopg@lists.postgresql.org; Sun, 08 Nov 2020 22:22:07 +0000 Received: from mail-lf1-x12a.google.com ([2a00:1450:4864:20::12a]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1kbt4X-0002hb-7P for psycopg@postgresql.org; Sun, 08 Nov 2020 22:22:07 +0000 Received: by mail-lf1-x12a.google.com with SMTP id 74so9679178lfo.5 for ; Sun, 08 Nov 2020 14:22:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=QG74gf7+tzHPakBc+6s8oD0+dAGeP54ztfThHiZSjWY=; b=eWXsGlr+gApSHQOii9MMCs+gpJsQHHXaf2QOgckRIamVo3qd20o24dRMICE3FpX5WY 48WTodrmP+sqsFR0fI3yjTjM9bxxJL40KnJNocLVxWwene4yArEHrIKfBFHJuBkPAaah dv+JJmDdxJ7XK1TJ6XAj+pOtssgC5buN+LW76cjHNTsHcOKBgp/H6QWu4KwGA1xfHoNP ZrwgcOa39oW/nptkoj76qm1vuJkm0jpY7RCPfkgXMQDtShKeUBt2w/8Yp/8nxJirnC2n lDXAPwWJIyeh71TCQkeb3rf28Cf7cLxm7ucfwe1oFyZXMPAz8BKnb/j3rkOB+6mEXdcd vz7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=QG74gf7+tzHPakBc+6s8oD0+dAGeP54ztfThHiZSjWY=; b=cBVWzc1oGSoio1TYtAJ+cRpCARAI3/+jVxDYQdP8kwleUXCo5SrdCaFETkYYujLSZL CjtY4uY39VIRhLMxynwAO9iB8gSPWLD0XGxHvID1Qo1KMM1mpGBu3i836j3U6br0FDvC VYrRIYNxcNxCEjXNyFNDyjT821KxP2dw0RwNWuXtQHAqcH1iX1gwPtdG7tw9XbAW/zd4 3jr5BaqwvlgRqpipuptgJIWxsJkrfNy+4dOmdA3df0L0Iac2GJROjRXAXF89e1waFxH8 54E5XY7T5KVJd5aFYv77rzT77gMKpzVkssCBRAS4FKJwP/8/GMwqev9U30lLTnd13IdW Q09A== X-Gm-Message-State: AOAM532iVbcWjKgqdpJnR6VxRlrGM9EGnnSq9MUqRq9SZROGPqBp7pND ecI6op3G3bErH1EgFUZ2h8ypkm36zDpp2rdd4VioKo/56CZ74Q== X-Google-Smtp-Source: ABdhPJzzAPB3EOG4NOwBdrG8khM5cSF8YvKh1aV7I0A0AIHwD2aowRk9TM47p4PoatgwTNGdWDXInRK//z3hbTjyH6Y= X-Received: by 2002:ac2:46ef:: with SMTP id q15mr2092205lfo.2.1604874124377; Sun, 08 Nov 2020 14:22:04 -0800 (PST) MIME-Version: 1.0 References: <2b9859f0-0964-2baa-b6bc-13f975ae0f67@aklaver.com> <88fb7a7e-a182-a816-c1a7-8a1f54b65215@aklaver.com> In-Reply-To: <88fb7a7e-a182-a816-c1a7-8a1f54b65215@aklaver.com> From: Daniele Varrazzo Date: Sun, 8 Nov 2020 22:21:53 +0000 Message-ID: Subject: Re: psycopg3 and adaptation choices To: Adrian Klaver Cc: psycopg@postgresql.org Content-Type: text/plain; charset="UTF-8" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk On Sun, 8 Nov 2020 at 20:35, Adrian Klaver wrote: > Alright I understand now. > More below. > > > > > In psycopg3 the idea is to use a more advanced protocol, which > > separates query and parameters. It brings several benefits: can use > > prepared statements (send a query once, several parameters later), > > passing large data doesn't bloat the parser (the params don't hit the > > lexer/parser), can use binary format (useful to pass large binary > > blobs without escaping them in a textual form), the format of the data > > is more homogeneous (no need to quoting), so we can use Python objects > > in COPY instead of limiting the interface for the copy functions to > > file-like objects only. > > > > Both in psycopg2 and 3 there is an adaptation from Python types to > > Postgres string representation. In pg2 there is additional quoting, > > because apart from numbers and bools you need to quote a literal > > string to merge it to the query and make it syntactically valid. > > So the issue in the psycopg3 protocol is making the parameters that are > passed in separately match up correctly in type to what the server is > expecting(or can cast implicitly)? Yes, correct. What we have to choose is which Postgres oid to map to each Python type. Sometimes the mapping is trivial (e.g. `datetime.date` -> `date` in Postgres, `uuid.UUID` -> `uuid`...) Sometimes it might be ambiguous: is a `datetime.datetime` a `timestamp` or a `timestamptz`? In some cases we don't care (here we can say `timestamptz` no problem: if the Python datetime doesn't have tzinfo, Postgres will use the `TimeZone` setting). Sometimes it's messy: what Python type corresponds to a Postgres `jsonb`? It might be a dict, or a list, or types that have other representations too (numbers, strings, bools). In this case, as in psycopg2, there can be a wrapper, e.g. `Json`, to tell psycopg that this dict, or list, or whatever else, must be jsonified for the db. When there are mismatches, sometimes the database cast rules help (e.gi in the timestamp[tz] case). Sometimes not: if we say `text` to a jsonb field, it will raise an error. Sometimes a cast is automatic on inserting in a table but not on passing a function parameter. Numbers are messy, as they usually are: Python has int, float, Decimal, Postgres has int2, int4, int8, float4, float8, numeric. The mappings float -> float8 and Decimal -> numeric are more or less straightforward. `int` is not, as in Python it's unbounded. If you say `select 10` in psql, the server understands "unknown type, but a number", and can try if either int* or numeric fit the context. But we don't have the help from the syntax that psql has: because 10 doesn't have quotes, Postgres is sure that it is a number, and not a string, but executing query/params separately we lose that expressivity: we cannot quote the strings and not the number. So choices are: 1. If we specify `numeric` or `int8` as oid, inserting in an int field in a table will work ok, but some functions/operators won't (e.g. "1 >> %s"). 2. If we specify `int4` it would work for those few functions defined as `integer`, but if we try to write a number that doesn't fit in 32 bits into a Postgres bigint field I assume something will overflow along the way, even if both python and postgres can handle it. 3. If we specify `unknown` it might work more often, but `cursor.execute("select %s", [10]) will return the string "10" instead of a number. So I wonder what's the best compromise to do here: the less bad seems 1. 3. might work in more contexts, but it's a very counterintuitive behaviour, and roundtripping other objects (dates, uuid) works no problem: they don't come back as strings. -- Daniele