jschaf / pggen

Generate type-safe Go for any Postgres query. If Postgres can run the query, pggen can generate code for it.
MIT License
282 stars 26 forks source link

Add definitions for Postgres' bytea type #8

Closed ngld closed 3 years ago

ngld commented 3 years ago

I was surprised that pggen doesn't support bytea columns. Was that intentional? I've added the necessary type definitions and successfully used the result.

I've also tried to use --go-type 'bytea=github.com/jackc/pgtype.Bytea' but that doesn't work since pggen still complains about the missing OID: https://github.com/jschaf/pggen/blob/7f6f84eb1636947992128675bddf9b33c5e2d557/internal/pg/types.go#L355-L361

Did I misunderstand how --go-type is supposed to work or does it only allow remapping known OIDs to different Go types?

jschaf commented 3 years ago

Thank for the report and commit. I think there's 2 bugs here:

  1. pggen errors too early when it can't determine a type for an OID. The code you highlighted throws an error if a type is not an enum type, a composite type, or an already known type. Throwing an error is a bug because we might be able to match the type by name using the --go-type flag.

  2. No support for the known type bytea. Your PR fixes this bug. I copied all the OIDs from https://github.com/jackc/pgtype/blob/1e141d8c32939b0c0fb2fac854cd37fc543b7835/pgtype.go#L17 and used a SQL query to generate the first version of the mappings (since changed by regex manipulations). I'm not sure how I missed bytea; the OID is in the sql query (17):

WITH types AS (
  SELECT oid,
         typname,
         replace(
             replace(
                 replace(
                     replace(
                         replace(
                             replace(
                                 replace(replace(replace(replace(replace(
                                                                     replace(regexp_replace(typname, '_(.*)', '\1Array'), 'oid', 'OID'),
                                                                     'cidr', 'CIDR'),
                                                                 'bpchar', 'BPChar'),
                                                         'aclitem', 'ACLItem'),
                                                 'uuid', 'UUID'),
                                         'jsonb', 'JSONB'),
                                 'json', 'JSON'), 'char', 'QChar'), 'cid', 'CID'), 'xid', 'XID'), 'tid', 'TID'), 'acl',
             'ACL') AS cap_name
  FROM pg_type
  WHERE oid IN (
                16, 17, 18, 19, 20, 21, 23, 25, 26, 27, 28, 29, 114, 600, 601, 602,
                603, 604, 628, 650, 651, 700, 701, 718, 705, 829, 869, 1000, 1005,
                1007, 1009, 1001, 1014, 1015, 1016, 1021, 1022, 1033, 1034, 1041, 1042,
                1043, 1082, 1083, 1114, 1115, 1182, 1184, 1185, 1186, 1231, 1560,
                1562, 1700, 2249, 2950, 2951, 3802, 3807, 3912, 3904, 3906, 3908, 3910,
                3926
    )),
     go_types AS (
       SELECT oid, typname, upper(left(cap_name, 1)) || right(cap_name, -1) AS upper_name
       FROM types),
     decls AS (
       SELECT oid, typname AS pg_name, upper_name AS go_name, upper_name || 'OID' AS go_type
       FROM go_types
     )
SELECT string_agg('  ' || go_name || ' = Type{OID: pgtype.' || go_type || ', Name: "' || pg_name || '"}', E'\n')
FROM decls;

I'll merge your fix, add a test, and then fix the first bug.

jschaf commented 3 years ago

The first bug is fixed in 5c4f88a. I created a new pg.Type: UnknownType to delay unknown OID errors until the Go code generator has a chance to choose a mapping based on the name of the type from the --go-type flag.