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.