mmkal / pgkit

PostgreSQL🤝TypeScript monorepo. SQL client/admin UI/smart migrator/type generator/schema inspector

Home Page:https://pgkit.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

INSERT INTO with ON CONFLICT produces nullable type

gajus opened this issue · comments

import type {
  DatabasePoolType,
} from 'slonik';
import {
  sql,
} from 'slonik';
import type {
  ForeignSlackChannelType,
} from '../types';

export const upsertSlackChannel = async (
  connection: DatabasePoolType,
  slackTeamId: string,
  foreignSlackChannel: ForeignSlackChannelType,
): Promise<number> => {
  return await connection.oneFirst(sql<queries.UpsertSlackChannel>`
    INSERT INTO slack_channel
    (
      slack_team_id,
      foreign_slack_channel_id,
      name,
      private
    )
    VALUES
    (
      ${slackTeamId},
      ${foreignSlackChannel.id},
      ${foreignSlackChannel.name},
      ${foreignSlackChannel.private}
    )
    ON CONFLICT (slack_team_id, foreign_slack_channel_id) WHERE deleted_at IS NULL
    DO UPDATE SET
      name = excluded.name,
      private = excluded.private
    RETURNING id
  `);
};

export declare namespace queries {
    // Generated by @slonik/typegen

    /** - query: `INSERT INTO slack_channel ( slack_team_i... [truncated] ... private = excluded.private RETURNING id` */
    export interface UpsertSlackChannel {

        /** regtype: `integer` */
        id: (number) | null;
    }
}

Filing this for record keeping. Don't have an immediate solution.

A decent solution would be here to allow to annotate the query to indicate that type is not NULL, i.e.

RETURNING id /* NOT NULL */

Just thinking out loud how to handle cases where typegen cannot determine type.

This should be fine, if it's able to identify slack_channel as a table. There are already a few tests with update ... returning ... but I added one explicitly to check that there wasn't something wrong when adding on conflict: https://github.com/mmkal/slonik-tools/blob/test/returning/packages/typegen/test/returning.test.ts#L41-L60

If you're able to edit that test so it fails in similar way to your example, I can take a look.

Re annotating to indicate not null. I've thought about a few cases where something like this would be useful. But I'm hesitant to start adding magic SQL directives. I agree some, probably including this one, would be useful. However if you were willing to use a naming convention, you could do this on your own project without any changes in the library. Similar to the example in the readme:

const typegen = require('@slonik/typegen')

/** @type {import('@slonik/typegen').Options} */
module.exports.default = {
  writeTypes: queries => {
    queries.forEach(query => {
      query.fields.forEach(field => {
        if (field.name.endsWith('_nn') {
          field.nullability = 'assumed_not_null'
        }
      })
    })

    return typegen.defaults.defaultWriteTypes()(queries)
  }
}

Then you could write queries like

update foo returning bar as bar_nn

And their types would be non-null.

Could it be because table is empty?

No, the actual data is never queried. If you have a create table slack_channel query, and typegen config, that might help to narrow it down.

Closing as non reproducible. @gajus if you can find a way to reproduce I can reopen.