0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-24 22:05:56 -05:00
logto/packages/core/src/database/insert-into.ts

108 lines
3.1 KiB
TypeScript
Raw Normal View History

2022-10-21 13:14:17 +08:00
import type { GeneratedSchema, SchemaLike } from '@logto/schemas';
import { has } from '@silverhand/essentials';
2024-03-16 19:04:55 +08:00
import type { CommonQueryMethods, IdentifierSqlToken } from '@silverhand/slonik';
import { sql } from '@silverhand/slonik';
2021-08-30 11:30:54 +08:00
2022-11-21 16:38:24 +08:00
import { InsertionError } from '#src/errors/SlonikError/index.js';
import assertThat from '#src/utils/assert-that.js';
2024-03-16 14:44:32 +08:00
import {
type OmitAutoSetFields,
convertToIdentifiers,
excludeAutoSetFields,
convertToPrimitiveOrSql,
conditionalSql,
} from '#src/utils/sql.js';
2021-08-30 11:30:54 +08:00
const setExcluded = (...fields: IdentifierSqlToken[]) =>
2021-08-18 00:24:00 +08:00
sql.join(
fields.map((field) => sql`${field}=excluded.${field}`),
sql`, `
);
type OnConflict =
| {
fields: IdentifierSqlToken[];
setExcludedFields: IdentifierSqlToken[];
ignore?: false;
}
| {
ignore: true;
};
2021-08-18 00:24:00 +08:00
type InsertIntoConfigReturning = {
returning: true;
onConflict?: OnConflict;
};
type InsertIntoConfig = {
returning?: false;
onConflict?: OnConflict;
};
type BuildInsertInto = {
2023-10-11 16:41:31 +08:00
<
Key extends string,
CreateSchema extends Partial<SchemaLike<Key>>,
Schema extends SchemaLike<Key>,
>(
{ fieldKeys, ...rest }: GeneratedSchema<Key, CreateSchema, Schema>,
2021-08-18 00:24:00 +08:00
config: InsertIntoConfigReturning
): (data: OmitAutoSetFields<CreateSchema>) => Promise<Schema>;
2023-10-11 16:41:31 +08:00
<
Key extends string,
CreateSchema extends Partial<SchemaLike<Key>>,
Schema extends SchemaLike<Key>,
>(
{ fieldKeys, ...rest }: GeneratedSchema<Key, CreateSchema, Schema>,
2021-08-18 00:24:00 +08:00
config?: InsertIntoConfig
): (data: OmitAutoSetFields<CreateSchema>) => Promise<void>;
};
2021-08-18 00:24:00 +08:00
export const buildInsertIntoWithPool =
(pool: CommonQueryMethods): BuildInsertInto =>
2023-10-11 16:41:31 +08:00
<
Key extends string,
CreateSchema extends Partial<SchemaLike<Key>>,
Schema extends SchemaLike<Key>,
>(
schema: GeneratedSchema<Key, CreateSchema, Schema>,
config?: InsertIntoConfig | InsertIntoConfigReturning
) => {
const { fieldKeys, ...rest } = schema;
const { table, fields } = convertToIdentifiers(rest);
const keys = excludeAutoSetFields(fieldKeys);
const returning = Boolean(config?.returning);
const onConflict = config?.onConflict;
2021-08-18 00:24:00 +08:00
return async (data: OmitAutoSetFields<CreateSchema>): Promise<Schema | void> => {
const insertingKeys = keys.filter((key) => has(data, key));
const {
rows: [entry],
} = await pool.query<Schema>(sql`
insert into ${table} (${sql.join(
insertingKeys.map((key) => fields[key]),
sql`, `
)})
values (${sql.join(
insertingKeys.map((key) => convertToPrimitiveOrSql(key, data[key] ?? null)),
sql`, `
)})
${conditionalSql(onConflict, (config) =>
config.ignore
? sql`
on conflict do nothing
`
: sql`
on conflict (${sql.join(config.fields, sql`, `)}) do update
set ${setExcluded(...config.setExcludedFields)}
`
)}
${conditionalSql(returning, () => sql`returning *`)}
`);
2021-08-18 00:24:00 +08:00
2023-10-11 16:41:31 +08:00
assertThat(!returning || entry, new InsertionError<Key, CreateSchema, Schema>(schema, data));
2022-01-27 19:26:34 +08:00
return entry;
};
2021-08-18 00:24:00 +08:00
};