mirror of
https://github.com/immich-app/immich.git
synced 2025-12-25 22:37:36 -05:00
121 lines
3.6 KiB
TypeScript
121 lines
3.6 KiB
TypeScript
import { sql } from 'kysely';
|
|
import { jsonArrayFrom } from 'kysely/helpers/postgres';
|
|
import { ColumnType, DatabaseColumn, Reader } from 'src/sql-tools/types';
|
|
|
|
export const readColumns: Reader = async (ctx, db) => {
|
|
const columns = await db
|
|
.selectFrom('information_schema.columns as c')
|
|
.leftJoin('information_schema.element_types as o', (join) =>
|
|
join
|
|
.onRef('c.table_catalog', '=', 'o.object_catalog')
|
|
.onRef('c.table_schema', '=', 'o.object_schema')
|
|
.onRef('c.table_name', '=', 'o.object_name')
|
|
.on('o.object_type', '=', sql.lit('TABLE'))
|
|
.onRef('c.dtd_identifier', '=', 'o.collection_type_identifier'),
|
|
)
|
|
.leftJoin('pg_type as t', (join) =>
|
|
join.onRef('t.typname', '=', 'c.udt_name').on('c.data_type', '=', sql.lit('USER-DEFINED')),
|
|
)
|
|
.leftJoin('pg_enum as e', (join) => join.onRef('e.enumtypid', '=', 't.oid'))
|
|
.select([
|
|
'c.table_name',
|
|
'c.column_name',
|
|
|
|
// is ARRAY, USER-DEFINED, or data type
|
|
'c.data_type',
|
|
'c.column_default',
|
|
'c.is_nullable',
|
|
'c.character_maximum_length',
|
|
|
|
// number types
|
|
'c.numeric_precision',
|
|
'c.numeric_scale',
|
|
|
|
// date types
|
|
'c.datetime_precision',
|
|
|
|
// user defined type
|
|
'c.udt_catalog',
|
|
'c.udt_schema',
|
|
'c.udt_name',
|
|
|
|
// data type for ARRAYs
|
|
'o.data_type as array_type',
|
|
])
|
|
.where('table_schema', '=', ctx.schemaName)
|
|
.execute();
|
|
|
|
const enumRaw = await db
|
|
.selectFrom('pg_type')
|
|
.innerJoin('pg_namespace', (join) =>
|
|
join.onRef('pg_namespace.oid', '=', 'pg_type.typnamespace').on('pg_namespace.nspname', '=', ctx.schemaName),
|
|
)
|
|
.where('typtype', '=', sql.lit('e'))
|
|
.select((eb) => [
|
|
'pg_type.typname as name',
|
|
jsonArrayFrom(
|
|
eb.selectFrom('pg_enum as e').select(['e.enumlabel as value']).whereRef('e.enumtypid', '=', 'pg_type.oid'),
|
|
).as('values'),
|
|
])
|
|
.execute();
|
|
|
|
const enums = enumRaw.map((item) => ({ name: item.name, values: item.values.map(({ value }) => value) }));
|
|
for (const { name, values } of enums) {
|
|
ctx.enums.push({ name, values, synchronize: true });
|
|
}
|
|
|
|
const enumMap = Object.fromEntries(enums.map((e) => [e.name, e.values]));
|
|
// add columns to tables
|
|
for (const column of columns) {
|
|
const table = ctx.getTableByName(column.table_name);
|
|
if (!table) {
|
|
continue;
|
|
}
|
|
|
|
const columnName = column.column_name;
|
|
|
|
const item: DatabaseColumn = {
|
|
type: column.data_type as ColumnType,
|
|
// TODO infer this from PK constraints
|
|
primary: false,
|
|
name: columnName,
|
|
tableName: column.table_name,
|
|
nullable: column.is_nullable === 'YES',
|
|
isArray: column.array_type !== null,
|
|
numericPrecision: column.numeric_precision ?? undefined,
|
|
numericScale: column.numeric_scale ?? undefined,
|
|
length: column.character_maximum_length ?? undefined,
|
|
default: column.column_default ?? undefined,
|
|
synchronize: true,
|
|
};
|
|
|
|
const columnLabel = `${table.name}.${columnName}`;
|
|
|
|
switch (column.data_type) {
|
|
// array types
|
|
case 'ARRAY': {
|
|
if (!column.array_type) {
|
|
ctx.warnings.push(`Unable to find type for ${columnLabel} (ARRAY)`);
|
|
continue;
|
|
}
|
|
item.type = column.array_type as ColumnType;
|
|
break;
|
|
}
|
|
|
|
// enum types
|
|
case 'USER-DEFINED': {
|
|
if (!enumMap[column.udt_name]) {
|
|
ctx.warnings.push(`Unable to find type for ${columnLabel} (ENUM)`);
|
|
continue;
|
|
}
|
|
|
|
item.type = 'enum';
|
|
item.enumName = column.udt_name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
table.columns.push(item);
|
|
}
|
|
};
|