mirror of
https://github.com/immich-app/immich.git
synced 2026-02-21 22:30:36 +09:00
chore: migrate to sql-tools library
This commit is contained in:
committed by
Jason Rasmussen
parent
82c6302549
commit
cbb9e89e41
25
pnpm-lock.yaml
generated
25
pnpm-lock.yaml
generated
@@ -343,6 +343,9 @@ importers:
|
||||
'@extism/extism':
|
||||
specifier: 2.0.0-rc13
|
||||
version: 2.0.0-rc13
|
||||
'@immich/sql-tools':
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0
|
||||
'@nestjs/bullmq':
|
||||
specifier: ^11.0.1
|
||||
version: 11.0.4(@nestjs/common@11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.13)(bullmq@5.68.0)
|
||||
@@ -3013,6 +3016,9 @@ packages:
|
||||
'@immich/justified-layout-wasm@0.4.3':
|
||||
resolution: {integrity: sha512-fpcQ7zPhP3Cp1bEXhONVYSUeIANa2uzaQFGKufUZQo5FO7aFT77szTVChhlCy4XaVy5R4ZvgSkA/1TJmeORz7Q==}
|
||||
|
||||
'@immich/sql-tools@0.2.0':
|
||||
resolution: {integrity: sha512-AH0GRIUYrckNKuid5uO33vgRbGaznhRtArdQ91K310A1oUFjaoNzOaZyZhXwEmft3WYeC1bx4fdgUeois2QH5A==}
|
||||
|
||||
'@immich/svelte-markdown-preprocess@0.2.1':
|
||||
resolution: {integrity: sha512-mbr/g75lO8Zh+ELCuYrZP0XB4gf2UbK8rJcGYMYxFJJzMMunV+sm9FqtV1dbwW2dpXzCZGz1XPCEZ6oo526TbA==}
|
||||
peerDependencies:
|
||||
@@ -8291,6 +8297,10 @@ packages:
|
||||
postgres:
|
||||
optional: true
|
||||
|
||||
kysely@0.28.11:
|
||||
resolution: {integrity: sha512-zpGIFg0HuoC893rIjYX1BETkVWdDnzTzF5e0kWXJFg5lE0k1/LfNWBejrcnOFu8Q2Rfq/hTDTU7XLUM8QOrpzg==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
kysely@0.28.2:
|
||||
resolution: {integrity: sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@@ -14812,6 +14822,13 @@ snapshots:
|
||||
|
||||
'@immich/justified-layout-wasm@0.4.3': {}
|
||||
|
||||
'@immich/sql-tools@0.2.0':
|
||||
dependencies:
|
||||
kysely: 0.28.11
|
||||
kysely-postgres-js: 3.0.0(kysely@0.28.11)(postgres@3.4.8)
|
||||
pg-connection-string: 2.11.0
|
||||
postgres: 3.4.8
|
||||
|
||||
'@immich/svelte-markdown-preprocess@0.2.1(svelte@5.51.5)':
|
||||
dependencies:
|
||||
front-matter: 4.0.2
|
||||
@@ -20822,12 +20839,20 @@ snapshots:
|
||||
type-is: 2.0.1
|
||||
vary: 1.1.2
|
||||
|
||||
kysely-postgres-js@3.0.0(kysely@0.28.11)(postgres@3.4.8):
|
||||
dependencies:
|
||||
kysely: 0.28.11
|
||||
optionalDependencies:
|
||||
postgres: 3.4.8
|
||||
|
||||
kysely-postgres-js@3.0.0(kysely@0.28.2)(postgres@3.4.8):
|
||||
dependencies:
|
||||
kysely: 0.28.2
|
||||
optionalDependencies:
|
||||
postgres: 3.4.8
|
||||
|
||||
kysely@0.28.11: {}
|
||||
|
||||
kysely@0.28.2: {}
|
||||
|
||||
langium@3.3.1:
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@extism/extism": "2.0.0-rc13",
|
||||
"@immich/sql-tools": "^0.2.0",
|
||||
"@nestjs/bullmq": "^11.0.1",
|
||||
"@nestjs/common": "^11.0.4",
|
||||
"@nestjs/core": "^11.0.4",
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
#!/usr/bin/env node
|
||||
process.env.DB_URL = process.env.DB_URL || 'postgres://postgres:postgres@localhost:5432/immich';
|
||||
|
||||
import { schemaDiff, schemaFromCode, schemaFromDatabase } from '@immich/sql-tools';
|
||||
import { Kysely, sql } from 'kysely';
|
||||
import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { basename, dirname, extname, join } from 'node:path';
|
||||
import postgres from 'postgres';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { DatabaseRepository } from 'src/repositories/database.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import 'src/schema';
|
||||
import { schemaDiff, schemaFromCode, schemaFromDatabase } from 'src/sql-tools';
|
||||
import { asPostgresConnectionConfig, getKyselyConfig } from 'src/utils/database';
|
||||
import { getKyselyConfig } from 'src/utils/database';
|
||||
|
||||
const main = async () => {
|
||||
const command = process.argv[2];
|
||||
@@ -130,10 +129,9 @@ const create = (path: string, up: string[], down: string[]) => {
|
||||
const compare = async () => {
|
||||
const configRepository = new ConfigRepository();
|
||||
const { database } = configRepository.getEnv();
|
||||
const db = postgres(asPostgresConnectionConfig(database.config));
|
||||
|
||||
const source = schemaFromCode({ overrides: true, namingStrategy: 'default' });
|
||||
const target = await schemaFromDatabase(db, {});
|
||||
const target = await schemaFromDatabase({ connection: database.config });
|
||||
|
||||
console.log(source.warnings.join('\n'));
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { asHuman } from '@immich/sql-tools';
|
||||
import { Command, CommandRunner } from 'nest-commander';
|
||||
import { ErrorMessages } from 'src/constants';
|
||||
import { CliService } from 'src/services/cli.service';
|
||||
import { asHuman } from 'src/sql-tools/schema-diff';
|
||||
|
||||
@Command({
|
||||
name: 'schema-check',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { BeforeUpdateTrigger, Column, ColumnOptions } from '@immich/sql-tools';
|
||||
import { SetMetadata, applyDecorators } from '@nestjs/common';
|
||||
import { ApiOperation, ApiOperationOptions, ApiProperty, ApiPropertyOptions, ApiTags } from '@nestjs/swagger';
|
||||
import _ from 'lodash';
|
||||
import { ApiCustomExtension, ApiTag, ImmichWorker, JobName, MetadataKey, QueueName } from 'src/enum';
|
||||
import { EmitEvent } from 'src/repositories/event.repository';
|
||||
import { immich_uuid_v7, updated_at } from 'src/schema/functions';
|
||||
import { BeforeUpdateTrigger, Column, ColumnOptions } from 'src/sql-tools';
|
||||
import { setUnion } from 'src/utils/set';
|
||||
|
||||
const GeneratedUuidV7Column = (options: Omit<ColumnOptions, 'type' | 'default' | 'nullable'> = {}) =>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { DatabaseSslMode } from '@immich/sql-tools';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import { IsEnum, IsInt, IsString, Matches } from 'class-validator';
|
||||
import { DatabaseSslMode, ImmichEnvironment, LogFormat, LogLevel } from 'src/enum';
|
||||
import { ImmichEnvironment, LogFormat, LogLevel } from 'src/enum';
|
||||
import { IsIPRange, Optional, ValidateBoolean } from 'src/validation';
|
||||
|
||||
export class EnvDto {
|
||||
|
||||
@@ -821,14 +821,6 @@ export enum OAuthTokenEndpointAuthMethod {
|
||||
ClientSecretBasic = 'client_secret_basic',
|
||||
}
|
||||
|
||||
export enum DatabaseSslMode {
|
||||
Disable = 'disable',
|
||||
Allow = 'allow',
|
||||
Prefer = 'prefer',
|
||||
Require = 'require',
|
||||
VerifyFull = 'verify-full',
|
||||
}
|
||||
|
||||
export enum AssetVisibility {
|
||||
Archive = 'archive',
|
||||
Timeline = 'timeline',
|
||||
|
||||
@@ -52,9 +52,9 @@ class Workers {
|
||||
try {
|
||||
const value = await systemMetadataRepository.get(SystemMetadataKey.MaintenanceMode);
|
||||
return value?.isMaintenanceMode || false;
|
||||
} catch (error) {
|
||||
} catch (error: Error | any) {
|
||||
// Table doesn't exist (migrations haven't run yet)
|
||||
if (error instanceof PostgresError && error.code === '42P01') {
|
||||
if ((error as PostgresError).code === '42P01') {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DatabaseConnectionParams } from '@immich/sql-tools';
|
||||
import { RegisterQueueOptions } from '@nestjs/bullmq';
|
||||
import { Inject, Injectable, Optional } from '@nestjs/common';
|
||||
import { QueueOptions } from 'bullmq';
|
||||
@@ -21,7 +22,7 @@ import {
|
||||
LogLevel,
|
||||
QueueName,
|
||||
} from 'src/enum';
|
||||
import { DatabaseConnectionParams, VectorExtension } from 'src/types';
|
||||
import { VectorExtension } from 'src/types';
|
||||
import { setDifference } from 'src/utils/set';
|
||||
|
||||
export interface EnvData {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { schemaDiff, schemaFromCode, schemaFromDatabase } from '@immich/sql-tools';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import AsyncLock from 'async-lock';
|
||||
import { FileMigrationProvider, Kysely, Migrator, sql, Transaction } from 'kysely';
|
||||
@@ -21,7 +22,6 @@ import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import 'src/schema'; // make sure all schema definitions are imported for schemaFromCode
|
||||
import { DB } from 'src/schema';
|
||||
import { schemaDiff, schemaFromCode, schemaFromDatabase } from 'src/sql-tools';
|
||||
import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types';
|
||||
import { vectorIndexQuery } from 'src/utils/database';
|
||||
import { isValidInteger } from 'src/validation';
|
||||
@@ -289,7 +289,8 @@ export class DatabaseRepository {
|
||||
|
||||
async getSchemaDrift() {
|
||||
const source = schemaFromCode({ overrides: true, namingStrategy: 'default' });
|
||||
const target = await schemaFromDatabase(this.db, {});
|
||||
const { database } = this.configRepository.getEnv();
|
||||
const target = await schemaFromDatabase({ connection: database.config });
|
||||
|
||||
const drift = schemaDiff(source, target, {
|
||||
tables: { ignoreExtra: true },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { registerEnum } from '@immich/sql-tools';
|
||||
import { AssetStatus, AssetVisibility, SourceType } from 'src/enum';
|
||||
import { registerEnum } from 'src/sql-tools';
|
||||
|
||||
export const assets_status_enum = registerEnum({
|
||||
name: 'assets_status_enum',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { registerFunction } from 'src/sql-tools';
|
||||
import { registerFunction } from '@immich/sql-tools';
|
||||
|
||||
export const immich_uuid_v7 = registerFunction({
|
||||
name: 'immich_uuid_v7',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Database, Extensions, Generated, Int8 } from '@immich/sql-tools';
|
||||
import { asset_face_source_type, asset_visibility_enum, assets_status_enum } from 'src/schema/enums';
|
||||
import {
|
||||
album_delete_audit,
|
||||
@@ -72,7 +73,6 @@ import { UserMetadataTable } from 'src/schema/tables/user-metadata.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import { VersionHistoryTable } from 'src/schema/tables/version-history.table';
|
||||
import { WorkflowActionTable, WorkflowFilterTable, WorkflowTable } from 'src/schema/tables/workflow.table';
|
||||
import { Database, Extensions, Generated, Int8 } from 'src/sql-tools';
|
||||
|
||||
@Extensions(['uuid-ossp', 'unaccent', 'cube', 'earthdistance', 'pg_trgm', 'plpgsql'])
|
||||
@Database({ name: 'immich' })
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AlbumAssetTable } from 'src/schema/tables/album-asset.table';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
Check,
|
||||
Column,
|
||||
@@ -15,7 +10,12 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AlbumAssetTable } from 'src/schema/tables/album-asset.table';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table('activity')
|
||||
@UpdatedAtTrigger('activity_updatedAt')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Column, CreateDateColumn, ForeignKeyColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { Column, CreateDateColumn, ForeignKeyColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('album_asset_audit')
|
||||
export class AlbumAssetAuditTable {
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { album_asset_delete_audit } from 'src/schema/functions';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
CreateDateColumn,
|
||||
@@ -10,7 +6,11 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { album_asset_delete_audit } from 'src/schema/functions';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
|
||||
@Table({ name: 'album_asset' })
|
||||
@UpdatedAtTrigger('album_asset_updatedAt')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('album_audit')
|
||||
export class AlbumAuditTable {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('album_user_audit')
|
||||
export class AlbumUserAuditTable {
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
import { CreateIdColumn, UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AlbumUserRole } from 'src/enum';
|
||||
import { album_user_after_insert, album_user_delete_audit } from 'src/schema/functions';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
AfterInsertTrigger,
|
||||
@@ -13,7 +8,12 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { CreateIdColumn, UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AlbumUserRole } from 'src/enum';
|
||||
import { album_user_after_insert, album_user_delete_audit } from 'src/schema/functions';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table({ name: 'album_user' })
|
||||
// Pre-existing indices from original album <--> user ManyToMany mapping
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AssetOrder } from 'src/enum';
|
||||
import { album_delete_audit } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
Column,
|
||||
@@ -14,7 +9,12 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AssetOrder } from 'src/enum';
|
||||
import { album_delete_audit } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table({ name: 'album' })
|
||||
@UpdatedAtTrigger('album_updatedAt')
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { Permission } from 'src/enum';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
@@ -10,7 +7,10 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { Permission } from 'src/enum';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table('api_key')
|
||||
@UpdatedAtTrigger('api_key_updatedAt')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('asset_audit')
|
||||
export class AssetAuditTable {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { AssetEditAction, AssetEditActionParameter } from 'src/dtos/editing.dto';
|
||||
import { asset_edit_delete, asset_edit_insert } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
AfterInsertTrigger,
|
||||
@@ -10,7 +7,10 @@ import {
|
||||
PrimaryGeneratedColumn,
|
||||
Table,
|
||||
Unique,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { AssetEditAction, AssetEditActionParameter } from 'src/dtos/editing.dto';
|
||||
import { asset_edit_delete, asset_edit_insert } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
|
||||
@Table('asset_edit')
|
||||
@AfterInsertTrigger({ scope: 'statement', function: asset_edit_insert, referencingNewTableAs: 'inserted_edit' })
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Column, ForeignKeyColumn, Generated, Int8, Table, Timestamp, UpdateDateColumn } from '@immich/sql-tools';
|
||||
import { LockableProperty } from 'src/database';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { Column, ForeignKeyColumn, Generated, Int8, Table, Timestamp, UpdateDateColumn } from 'src/sql-tools';
|
||||
|
||||
@Table('asset_exif')
|
||||
@UpdatedAtTrigger('asset_exif_updatedAt')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('asset_face_audit')
|
||||
export class AssetFaceAuditTable {
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { SourceType } from 'src/enum';
|
||||
import { asset_face_source_type } from 'src/schema/enums';
|
||||
import { asset_face_audit } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { PersonTable } from 'src/schema/tables/person.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
Column,
|
||||
@@ -15,7 +9,13 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { SourceType } from 'src/enum';
|
||||
import { asset_face_source_type } from 'src/schema/enums';
|
||||
import { asset_face_audit } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { PersonTable } from 'src/schema/tables/person.table';
|
||||
|
||||
@Table({ name: 'asset_face' })
|
||||
@UpdatedAtTrigger('asset_face_updatedAt')
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AssetFileType } from 'src/enum';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
@@ -11,7 +8,10 @@ import {
|
||||
Timestamp,
|
||||
Unique,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AssetFileType } from 'src/enum';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
|
||||
@Table('asset_file')
|
||||
@Unique({ columns: ['assetId', 'type', 'isEdited'] })
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, ForeignKeyColumn, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { Column, ForeignKeyColumn, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('asset_job_status')
|
||||
export class AssetJobStatusTable {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('asset_metadata_audit')
|
||||
export class AssetMetadataAuditTable {
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AssetMetadataKey } from 'src/enum';
|
||||
import { asset_metadata_audit } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
Column,
|
||||
@@ -11,7 +7,11 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AssetMetadataKey } from 'src/enum';
|
||||
import { asset_metadata_audit } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
|
||||
@UpdatedAtTrigger('asset_metadata_updated_at')
|
||||
@Table('asset_metadata')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, ForeignKeyColumn, Generated, PrimaryGeneratedColumn, Table } from '@immich/sql-tools';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { Column, ForeignKeyColumn, Generated, PrimaryGeneratedColumn, Table } from 'src/sql-tools';
|
||||
|
||||
@Table('asset_ocr')
|
||||
export class AssetOcrTable {
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AssetStatus, AssetType, AssetVisibility } from 'src/enum';
|
||||
import { asset_visibility_enum, assets_status_enum } from 'src/schema/enums';
|
||||
import { asset_delete_audit } from 'src/schema/functions';
|
||||
import { LibraryTable } from 'src/schema/tables/library.table';
|
||||
import { StackTable } from 'src/schema/tables/stack.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
Column,
|
||||
@@ -17,7 +10,14 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { AssetStatus, AssetType, AssetVisibility } from 'src/enum';
|
||||
import { asset_visibility_enum, assets_status_enum } from 'src/schema/enums';
|
||||
import { asset_delete_audit } from 'src/schema/functions';
|
||||
import { LibraryTable } from 'src/schema/tables/library.table';
|
||||
import { StackTable } from 'src/schema/tables/stack.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import { ASSET_CHECKSUM_CONSTRAINT } from 'src/utils/database';
|
||||
|
||||
@Table('asset')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, CreateDateColumn, Generated, Index, PrimaryColumn, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { DatabaseAction, EntityType } from 'src/enum';
|
||||
import { Column, CreateDateColumn, Generated, Index, PrimaryColumn, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('audit')
|
||||
@Index({ columns: ['ownerId', 'createdAt'] })
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, ForeignKeyColumn, Index, Table } from '@immich/sql-tools';
|
||||
import { AssetFaceTable } from 'src/schema/tables/asset-face.table';
|
||||
import { Column, ForeignKeyColumn, Index, Table } from 'src/sql-tools';
|
||||
|
||||
@Table({ name: 'face_search' })
|
||||
@Index({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Column, Index, PrimaryColumn, Table, Timestamp } from 'src/sql-tools';
|
||||
import { Column, Index, PrimaryColumn, Table, Timestamp } from '@immich/sql-tools';
|
||||
|
||||
@Table({ name: 'geodata_places', primaryConstraintName: 'geodata_places_pkey' })
|
||||
@Index({
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
@@ -10,7 +8,9 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table('library')
|
||||
@UpdatedAtTrigger('library_updatedAt')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Column, CreateDateColumn, ForeignKeyColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { MemoryTable } from 'src/schema/tables/memory.table';
|
||||
import { Column, CreateDateColumn, ForeignKeyColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('memory_asset_audit')
|
||||
export class MemoryAssetAuditTable {
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { memory_asset_delete_audit } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { MemoryTable } from 'src/schema/tables/memory.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
CreateDateColumn,
|
||||
@@ -10,7 +6,11 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { memory_asset_delete_audit } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { MemoryTable } from 'src/schema/tables/memory.table';
|
||||
|
||||
@Table('memory_asset')
|
||||
@UpdatedAtTrigger('memory_asset_updatedAt')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('memory_audit')
|
||||
export class MemoryAuditTable {
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { MemoryType } from 'src/enum';
|
||||
import { memory_delete_audit } from 'src/schema/functions';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
Column,
|
||||
@@ -13,7 +9,11 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { MemoryType } from 'src/enum';
|
||||
import { memory_delete_audit } from 'src/schema/functions';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table('memory')
|
||||
@UpdatedAtTrigger('memory_updatedAt')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, Generated, PrimaryGeneratedColumn, Table, Unique } from '@immich/sql-tools';
|
||||
import { PathType } from 'src/enum';
|
||||
import { Column, Generated, PrimaryGeneratedColumn, Table, Unique } from 'src/sql-tools';
|
||||
|
||||
@Table('move_history')
|
||||
// path lock (per entity)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Column, Generated, PrimaryGeneratedColumn, Table } from 'src/sql-tools';
|
||||
import { Column, Generated, PrimaryGeneratedColumn, Table } from '@immich/sql-tools';
|
||||
|
||||
@Table({ name: 'naturalearth_countries', primaryConstraintName: 'naturalearth_countries_pkey' })
|
||||
export class NaturalEarthCountriesTable {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { NotificationLevel, NotificationType } from 'src/enum';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
@@ -11,7 +8,10 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { NotificationLevel, NotificationType } from 'src/enum';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table('notification')
|
||||
@UpdatedAtTrigger('notification_updatedAt')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, ForeignKeyColumn, Index, Table } from '@immich/sql-tools';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { Column, ForeignKeyColumn, Index, Table } from 'src/sql-tools';
|
||||
|
||||
@Table('ocr_search')
|
||||
@Index({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('partner_audit')
|
||||
export class PartnerAuditTable {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { CreateIdColumn, UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { partner_delete_audit } from 'src/schema/functions';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
Column,
|
||||
@@ -10,7 +7,10 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { CreateIdColumn, UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { partner_delete_audit } from 'src/schema/functions';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table('partner')
|
||||
@UpdatedAtTrigger('partner_updatedAt')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('person_audit')
|
||||
export class PersonAuditTable {
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { person_delete_audit } from 'src/schema/functions';
|
||||
import { AssetFaceTable } from 'src/schema/tables/asset-face.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
Check,
|
||||
@@ -13,7 +9,11 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { person_delete_audit } from 'src/schema/functions';
|
||||
import { AssetFaceTable } from 'src/schema/tables/asset-face.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table('person')
|
||||
@UpdatedAtTrigger('person_updatedAt')
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { PluginContext } from 'src/enum';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
@@ -9,7 +8,8 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { PluginContext } from 'src/enum';
|
||||
import type { JSONSchema } from 'src/types/plugin-schema.types';
|
||||
|
||||
@Table('plugin')
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
@@ -9,7 +7,9 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table({ name: 'session' })
|
||||
@UpdatedAtTrigger('session_updatedAt')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForeignKeyColumn, Table } from '@immich/sql-tools';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { SharedLinkTable } from 'src/schema/tables/shared-link.table';
|
||||
import { ForeignKeyColumn, Table } from 'src/sql-tools';
|
||||
|
||||
@Table('shared_link_asset')
|
||||
export class SharedLinkAssetTable {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { SharedLinkType } from 'src/enum';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
@@ -9,7 +6,10 @@ import {
|
||||
PrimaryGeneratedColumn,
|
||||
Table,
|
||||
Timestamp,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { SharedLinkType } from 'src/enum';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table('shared_link')
|
||||
export class SharedLinkTable {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, ForeignKeyColumn, Index, Table } from '@immich/sql-tools';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { Column, ForeignKeyColumn, Index, Table } from 'src/sql-tools';
|
||||
|
||||
@Table({ name: 'smart_search' })
|
||||
@Index({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('stack_audit')
|
||||
export class StackAuditTable {
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { stack_delete_audit } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
CreateDateColumn,
|
||||
@@ -11,7 +7,11 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { stack_delete_audit } from 'src/schema/functions';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table('stack')
|
||||
@UpdatedAtTrigger('stack_updatedAt')
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { SyncEntityType } from 'src/enum';
|
||||
import { SessionTable } from 'src/schema/tables/session.table';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
@@ -10,7 +7,10 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { SyncEntityType } from 'src/enum';
|
||||
import { SessionTable } from 'src/schema/tables/session.table';
|
||||
|
||||
@Table('session_sync_checkpoint')
|
||||
@UpdatedAtTrigger('session_sync_checkpoint_updatedAt')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, PrimaryColumn, Table } from '@immich/sql-tools';
|
||||
import { SystemMetadataKey } from 'src/enum';
|
||||
import { Column, PrimaryColumn, Table } from 'src/sql-tools';
|
||||
import { SystemMetadata } from 'src/types';
|
||||
|
||||
@Table('system_metadata')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForeignKeyColumn, Index, Table } from '@immich/sql-tools';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { TagTable } from 'src/schema/tables/tag.table';
|
||||
import { ForeignKeyColumn, Index, Table } from 'src/sql-tools';
|
||||
|
||||
@Index({ columns: ['assetId', 'tagId'] })
|
||||
@Table('tag_asset')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ForeignKeyColumn, Table } from '@immich/sql-tools';
|
||||
import { TagTable } from 'src/schema/tables/tag.table';
|
||||
import { ForeignKeyColumn, Table } from 'src/sql-tools';
|
||||
|
||||
@Table('tag_closure')
|
||||
export class TagClosureTable {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
@@ -10,7 +8,9 @@ import {
|
||||
Timestamp,
|
||||
Unique,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
@Table('tag')
|
||||
@UpdatedAtTrigger('tag_updatedAt')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('user_audit')
|
||||
export class UserAuditTable {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from '@immich/sql-tools';
|
||||
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||
import { UserMetadataKey } from 'src/enum';
|
||||
import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools';
|
||||
|
||||
@Table('user_metadata_audit')
|
||||
export class UserMetadataAuditTable {
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { UserMetadataKey } from 'src/enum';
|
||||
import { user_metadata_audit } from 'src/schema/functions';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
Column,
|
||||
@@ -11,7 +7,11 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { UserMetadataKey } from 'src/enum';
|
||||
import { user_metadata_audit } from 'src/schema/functions';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import { UserMetadata, UserMetadataItem } from 'src/types';
|
||||
|
||||
@UpdatedAtTrigger('user_metadata_updated_at')
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { ColumnType } from 'kysely';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { UserAvatarColor, UserStatus } from 'src/enum';
|
||||
import { user_delete_audit } from 'src/schema/functions';
|
||||
import {
|
||||
AfterDeleteTrigger,
|
||||
Column,
|
||||
@@ -13,7 +9,11 @@ import {
|
||||
Table,
|
||||
Timestamp,
|
||||
UpdateDateColumn,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { ColumnType } from 'kysely';
|
||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||
import { UserAvatarColor, UserStatus } from 'src/enum';
|
||||
import { user_delete_audit } from 'src/schema/functions';
|
||||
|
||||
@Table('user')
|
||||
@UpdatedAtTrigger('user_updatedAt')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Column, CreateDateColumn, Generated, PrimaryGeneratedColumn, Table, Timestamp } from 'src/sql-tools';
|
||||
import { Column, CreateDateColumn, Generated, PrimaryGeneratedColumn, Table, Timestamp } from '@immich/sql-tools';
|
||||
|
||||
@Table('version_history')
|
||||
export class VersionHistoryTable {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { PluginTriggerType } from 'src/enum';
|
||||
import { PluginActionTable, PluginFilterTable } from 'src/schema/tables/plugin.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
@@ -10,7 +7,10 @@ import {
|
||||
PrimaryGeneratedColumn,
|
||||
Table,
|
||||
Timestamp,
|
||||
} from 'src/sql-tools';
|
||||
} from '@immich/sql-tools';
|
||||
import { PluginTriggerType } from 'src/enum';
|
||||
import { PluginActionTable, PluginFilterTable } from 'src/schema/tables/plugin.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import type { ActionConfig, FilterConfig } from 'src/types/plugin-schema.types';
|
||||
|
||||
@Table('workflow')
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { schemaDiff } from '@immich/sql-tools';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { isAbsolute, join } from 'node:path';
|
||||
import { SALT_ROUNDS } from 'src/constants';
|
||||
@@ -5,7 +6,6 @@ import { MaintenanceAuthDto } from 'src/dtos/maintenance.dto';
|
||||
import { UserAdminResponseDto, mapUserAdmin } from 'src/dtos/user.dto';
|
||||
import { MaintenanceAction, SystemMetadataKey } from 'src/enum';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
import { schemaDiff } from 'src/sql-tools';
|
||||
import { createMaintenanceLoginUrl, generateMaintenanceSecret } from 'src/utils/maintenance';
|
||||
import { getExternalDomain } from 'src/utils/misc';
|
||||
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
import { compareColumns } from 'src/sql-tools/comparers/column.comparer';
|
||||
import { DatabaseColumn, Reason } from 'src/sql-tools/types';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const testColumn: DatabaseColumn = {
|
||||
name: 'test',
|
||||
tableName: 'table1',
|
||||
primary: false,
|
||||
nullable: false,
|
||||
isArray: false,
|
||||
type: 'character varying',
|
||||
synchronize: true,
|
||||
};
|
||||
|
||||
describe('compareColumns', () => {
|
||||
describe('onExtra', () => {
|
||||
it('should work', () => {
|
||||
expect(compareColumns().onExtra(testColumn)).toEqual([
|
||||
{
|
||||
tableName: 'table1',
|
||||
columnName: 'test',
|
||||
type: 'ColumnDrop',
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onMissing', () => {
|
||||
it('should work', () => {
|
||||
expect(compareColumns().onMissing(testColumn)).toEqual([
|
||||
{
|
||||
type: 'ColumnAdd',
|
||||
column: testColumn,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCompare', () => {
|
||||
it('should work', () => {
|
||||
expect(compareColumns().onCompare(testColumn, testColumn)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should detect a change in type', () => {
|
||||
const source: DatabaseColumn = { ...testColumn };
|
||||
const target: DatabaseColumn = { ...testColumn, type: 'text' };
|
||||
const reason = 'column type is different (character varying vs text)';
|
||||
expect(compareColumns().onCompare(source, target)).toEqual([
|
||||
{
|
||||
columnName: 'test',
|
||||
tableName: 'table1',
|
||||
type: 'ColumnDrop',
|
||||
reason,
|
||||
},
|
||||
{
|
||||
type: 'ColumnAdd',
|
||||
column: source,
|
||||
reason,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should detect a change in default', () => {
|
||||
const source: DatabaseColumn = { ...testColumn, nullable: true };
|
||||
const target: DatabaseColumn = { ...testColumn, nullable: true, default: "''" };
|
||||
const reason = `default is different (null vs '')`;
|
||||
expect(compareColumns().onCompare(source, target)).toEqual([
|
||||
{
|
||||
columnName: 'test',
|
||||
tableName: 'table1',
|
||||
type: 'ColumnAlter',
|
||||
changes: {
|
||||
default: 'NULL',
|
||||
},
|
||||
reason,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should detect a comment change', () => {
|
||||
const source: DatabaseColumn = { ...testColumn, comment: 'new comment' };
|
||||
const target: DatabaseColumn = { ...testColumn, comment: 'old comment' };
|
||||
const reason = 'comment is different (new comment vs old comment)';
|
||||
expect(compareColumns().onCompare(source, target)).toEqual([
|
||||
{
|
||||
columnName: 'test',
|
||||
tableName: 'table1',
|
||||
type: 'ColumnAlter',
|
||||
changes: {
|
||||
comment: 'new comment',
|
||||
},
|
||||
reason,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,108 +0,0 @@
|
||||
import { asRenameKey, getColumnType, isDefaultEqual } from 'src/sql-tools/helpers';
|
||||
import { Comparer, DatabaseColumn, Reason, SchemaDiff } from 'src/sql-tools/types';
|
||||
|
||||
export const compareColumns = () =>
|
||||
({
|
||||
getRenameKey: (column) => {
|
||||
return asRenameKey([
|
||||
column.tableName,
|
||||
column.type,
|
||||
column.nullable,
|
||||
column.default,
|
||||
column.storage,
|
||||
column.primary,
|
||||
column.isArray,
|
||||
column.length,
|
||||
column.identity,
|
||||
column.enumName,
|
||||
column.numericPrecision,
|
||||
column.numericScale,
|
||||
]);
|
||||
},
|
||||
onRename: (source, target) => [
|
||||
{
|
||||
type: 'ColumnRename',
|
||||
tableName: source.tableName,
|
||||
oldName: target.name,
|
||||
newName: source.name,
|
||||
reason: Reason.Rename,
|
||||
},
|
||||
],
|
||||
onMissing: (source) => [
|
||||
{
|
||||
type: 'ColumnAdd',
|
||||
column: source,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
],
|
||||
onExtra: (target) => [
|
||||
{
|
||||
type: 'ColumnDrop',
|
||||
tableName: target.tableName,
|
||||
columnName: target.name,
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
],
|
||||
onCompare: (source, target) => {
|
||||
const sourceType = getColumnType(source);
|
||||
const targetType = getColumnType(target);
|
||||
|
||||
const isTypeChanged = sourceType !== targetType;
|
||||
|
||||
if (isTypeChanged) {
|
||||
// TODO: convert between types via UPDATE when possible
|
||||
return dropAndRecreateColumn(source, target, `column type is different (${sourceType} vs ${targetType})`);
|
||||
}
|
||||
|
||||
const items: SchemaDiff[] = [];
|
||||
if (source.nullable !== target.nullable) {
|
||||
items.push({
|
||||
type: 'ColumnAlter',
|
||||
tableName: source.tableName,
|
||||
columnName: source.name,
|
||||
changes: {
|
||||
nullable: source.nullable,
|
||||
},
|
||||
reason: `nullable is different (${source.nullable} vs ${target.nullable})`,
|
||||
});
|
||||
}
|
||||
|
||||
if (!isDefaultEqual(source, target)) {
|
||||
items.push({
|
||||
type: 'ColumnAlter',
|
||||
tableName: source.tableName,
|
||||
columnName: source.name,
|
||||
changes: {
|
||||
default: String(source.default ?? 'NULL'),
|
||||
},
|
||||
reason: `default is different (${source.default ?? 'null'} vs ${target.default})`,
|
||||
});
|
||||
}
|
||||
|
||||
if (source.comment !== target.comment) {
|
||||
items.push({
|
||||
type: 'ColumnAlter',
|
||||
tableName: source.tableName,
|
||||
columnName: source.name,
|
||||
changes: {
|
||||
comment: String(source.comment),
|
||||
},
|
||||
reason: `comment is different (${source.comment} vs ${target.comment})`,
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
}) satisfies Comparer<DatabaseColumn>;
|
||||
|
||||
const dropAndRecreateColumn = (source: DatabaseColumn, target: DatabaseColumn, reason: string): SchemaDiff[] => {
|
||||
return [
|
||||
{
|
||||
type: 'ColumnDrop',
|
||||
tableName: target.tableName,
|
||||
columnName: target.name,
|
||||
reason,
|
||||
},
|
||||
{ type: 'ColumnAdd', column: source, reason },
|
||||
];
|
||||
};
|
||||
@@ -1,63 +0,0 @@
|
||||
import { compareConstraints } from 'src/sql-tools/comparers/constraint.comparer';
|
||||
import { ConstraintType, DatabaseConstraint, Reason } from 'src/sql-tools/types';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const testConstraint: DatabaseConstraint = {
|
||||
type: ConstraintType.PRIMARY_KEY,
|
||||
name: 'test',
|
||||
tableName: 'table1',
|
||||
columnNames: ['column1'],
|
||||
synchronize: true,
|
||||
};
|
||||
|
||||
describe('compareConstraints', () => {
|
||||
describe('onExtra', () => {
|
||||
it('should work', () => {
|
||||
expect(compareConstraints().onExtra(testConstraint)).toEqual([
|
||||
{
|
||||
type: 'ConstraintDrop',
|
||||
constraintName: 'test',
|
||||
tableName: 'table1',
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onMissing', () => {
|
||||
it('should work', () => {
|
||||
expect(compareConstraints().onMissing(testConstraint)).toEqual([
|
||||
{
|
||||
type: 'ConstraintAdd',
|
||||
constraint: testConstraint,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCompare', () => {
|
||||
it('should work', () => {
|
||||
expect(compareConstraints().onCompare(testConstraint, testConstraint)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should detect a change in type', () => {
|
||||
const source: DatabaseConstraint = { ...testConstraint };
|
||||
const target: DatabaseConstraint = { ...testConstraint, columnNames: ['column1', 'column2'] };
|
||||
const reason = 'Primary key columns are different: (column1 vs column1,column2)';
|
||||
expect(compareConstraints().onCompare(source, target)).toEqual([
|
||||
{
|
||||
constraintName: 'test',
|
||||
tableName: 'table1',
|
||||
type: 'ConstraintDrop',
|
||||
reason,
|
||||
},
|
||||
{
|
||||
type: 'ConstraintAdd',
|
||||
constraint: source,
|
||||
reason,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,165 +0,0 @@
|
||||
import { asRenameKey, haveEqualColumns } from 'src/sql-tools/helpers';
|
||||
import {
|
||||
CompareFunction,
|
||||
Comparer,
|
||||
ConstraintType,
|
||||
DatabaseCheckConstraint,
|
||||
DatabaseConstraint,
|
||||
DatabaseForeignKeyConstraint,
|
||||
DatabasePrimaryKeyConstraint,
|
||||
DatabaseUniqueConstraint,
|
||||
Reason,
|
||||
SchemaDiff,
|
||||
} from 'src/sql-tools/types';
|
||||
|
||||
export const compareConstraints = (): Comparer<DatabaseConstraint> => ({
|
||||
getRenameKey: (constraint) => {
|
||||
switch (constraint.type) {
|
||||
case ConstraintType.PRIMARY_KEY:
|
||||
case ConstraintType.UNIQUE: {
|
||||
return asRenameKey([constraint.type, constraint.tableName, ...constraint.columnNames.toSorted()]);
|
||||
}
|
||||
|
||||
case ConstraintType.FOREIGN_KEY: {
|
||||
return asRenameKey([
|
||||
constraint.type,
|
||||
constraint.tableName,
|
||||
...constraint.columnNames.toSorted(),
|
||||
constraint.referenceTableName,
|
||||
...constraint.referenceColumnNames.toSorted(),
|
||||
]);
|
||||
}
|
||||
|
||||
case ConstraintType.CHECK: {
|
||||
const expression = constraint.expression.replaceAll('(', '').replaceAll(')', '');
|
||||
return asRenameKey([constraint.type, constraint.tableName, expression]);
|
||||
}
|
||||
}
|
||||
},
|
||||
onRename: (source, target) => [
|
||||
{
|
||||
type: 'ConstraintRename',
|
||||
tableName: target.tableName,
|
||||
oldName: target.name,
|
||||
newName: source.name,
|
||||
reason: Reason.Rename,
|
||||
},
|
||||
],
|
||||
onMissing: (source) => [
|
||||
{
|
||||
type: 'ConstraintAdd',
|
||||
constraint: source,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
],
|
||||
onExtra: (target) => [
|
||||
{
|
||||
type: 'ConstraintDrop',
|
||||
tableName: target.tableName,
|
||||
constraintName: target.name,
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
],
|
||||
onCompare: (source, target) => {
|
||||
switch (source.type) {
|
||||
case ConstraintType.PRIMARY_KEY: {
|
||||
return comparePrimaryKeyConstraint(source, target as DatabasePrimaryKeyConstraint);
|
||||
}
|
||||
|
||||
case ConstraintType.FOREIGN_KEY: {
|
||||
return compareForeignKeyConstraint(source, target as DatabaseForeignKeyConstraint);
|
||||
}
|
||||
|
||||
case ConstraintType.UNIQUE: {
|
||||
return compareUniqueConstraint(source, target as DatabaseUniqueConstraint);
|
||||
}
|
||||
|
||||
case ConstraintType.CHECK: {
|
||||
return compareCheckConstraint(source, target as DatabaseCheckConstraint);
|
||||
}
|
||||
|
||||
default: {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const comparePrimaryKeyConstraint: CompareFunction<DatabasePrimaryKeyConstraint> = (source, target) => {
|
||||
if (!haveEqualColumns(source.columnNames, target.columnNames)) {
|
||||
return dropAndRecreateConstraint(
|
||||
source,
|
||||
target,
|
||||
`Primary key columns are different: (${source.columnNames} vs ${target.columnNames})`,
|
||||
);
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
const compareForeignKeyConstraint: CompareFunction<DatabaseForeignKeyConstraint> = (source, target) => {
|
||||
let reason = '';
|
||||
|
||||
const sourceDeleteAction = source.onDelete ?? 'NO ACTION';
|
||||
const targetDeleteAction = target.onDelete ?? 'NO ACTION';
|
||||
|
||||
const sourceUpdateAction = source.onUpdate ?? 'NO ACTION';
|
||||
const targetUpdateAction = target.onUpdate ?? 'NO ACTION';
|
||||
|
||||
if (!haveEqualColumns(source.columnNames, target.columnNames)) {
|
||||
reason = `columns are different (${source.columnNames} vs ${target.columnNames})`;
|
||||
} else if (!haveEqualColumns(source.referenceColumnNames, target.referenceColumnNames)) {
|
||||
reason = `reference columns are different (${source.referenceColumnNames} vs ${target.referenceColumnNames})`;
|
||||
} else if (source.referenceTableName !== target.referenceTableName) {
|
||||
reason = `reference table is different (${source.referenceTableName} vs ${target.referenceTableName})`;
|
||||
} else if (sourceDeleteAction !== targetDeleteAction) {
|
||||
reason = `ON DELETE action is different (${sourceDeleteAction} vs ${targetDeleteAction})`;
|
||||
} else if (sourceUpdateAction !== targetUpdateAction) {
|
||||
reason = `ON UPDATE action is different (${sourceUpdateAction} vs ${targetUpdateAction})`;
|
||||
}
|
||||
|
||||
if (reason) {
|
||||
return dropAndRecreateConstraint(source, target, reason);
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
const compareUniqueConstraint: CompareFunction<DatabaseUniqueConstraint> = (source, target) => {
|
||||
let reason = '';
|
||||
|
||||
if (!haveEqualColumns(source.columnNames, target.columnNames)) {
|
||||
reason = `columns are different (${source.columnNames} vs ${target.columnNames})`;
|
||||
}
|
||||
|
||||
if (reason) {
|
||||
return dropAndRecreateConstraint(source, target, reason);
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
const compareCheckConstraint: CompareFunction<DatabaseCheckConstraint> = (source, target) => {
|
||||
if (source.expression !== target.expression) {
|
||||
// comparing expressions is hard because postgres reconstructs it with different formatting
|
||||
// for now if the constraint exists with the same name, we will just skip it
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
const dropAndRecreateConstraint = (
|
||||
source: DatabaseConstraint,
|
||||
target: DatabaseConstraint,
|
||||
reason: string,
|
||||
): SchemaDiff[] => {
|
||||
return [
|
||||
{
|
||||
type: 'ConstraintDrop',
|
||||
tableName: target.tableName,
|
||||
constraintName: target.name,
|
||||
reason,
|
||||
},
|
||||
{ type: 'ConstraintAdd', constraint: source, reason },
|
||||
];
|
||||
};
|
||||
@@ -1,54 +0,0 @@
|
||||
import { compareEnums } from 'src/sql-tools/comparers/enum.comparer';
|
||||
import { DatabaseEnum, Reason } from 'src/sql-tools/types';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const testEnum: DatabaseEnum = { name: 'test', values: ['foo', 'bar'], synchronize: true };
|
||||
|
||||
describe('compareEnums', () => {
|
||||
describe('onExtra', () => {
|
||||
it('should work', () => {
|
||||
expect(compareEnums().onExtra(testEnum)).toEqual([
|
||||
{
|
||||
enumName: 'test',
|
||||
type: 'EnumDrop',
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onMissing', () => {
|
||||
it('should work', () => {
|
||||
expect(compareEnums().onMissing(testEnum)).toEqual([
|
||||
{
|
||||
type: 'EnumCreate',
|
||||
enum: testEnum,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCompare', () => {
|
||||
it('should work', () => {
|
||||
expect(compareEnums().onCompare(testEnum, testEnum)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should drop and recreate when values list is different', () => {
|
||||
const source = { name: 'test', values: ['foo', 'bar'], synchronize: true };
|
||||
const target = { name: 'test', values: ['foo', 'bar', 'world'], synchronize: true };
|
||||
expect(compareEnums().onCompare(source, target)).toEqual([
|
||||
{
|
||||
enumName: 'test',
|
||||
type: 'EnumDrop',
|
||||
reason: 'enum values has changed (foo,bar vs foo,bar,world)',
|
||||
},
|
||||
{
|
||||
type: 'EnumCreate',
|
||||
enum: source,
|
||||
reason: 'enum values has changed (foo,bar vs foo,bar,world)',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import { Comparer, DatabaseEnum, Reason } from 'src/sql-tools/types';
|
||||
|
||||
export const compareEnums = (): Comparer<DatabaseEnum> => ({
|
||||
onMissing: (source) => [
|
||||
{
|
||||
type: 'EnumCreate',
|
||||
enum: source,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
],
|
||||
onExtra: (target) => [
|
||||
{
|
||||
type: 'EnumDrop',
|
||||
enumName: target.name,
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
],
|
||||
onCompare: (source, target) => {
|
||||
if (source.values.toString() !== target.values.toString()) {
|
||||
// TODO add or remove values if the lists are different or the order has changed
|
||||
const reason = `enum values has changed (${source.values} vs ${target.values})`;
|
||||
return [
|
||||
{
|
||||
type: 'EnumDrop',
|
||||
enumName: source.name,
|
||||
reason,
|
||||
},
|
||||
{
|
||||
type: 'EnumCreate',
|
||||
enum: source,
|
||||
reason,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
@@ -1,37 +0,0 @@
|
||||
import { compareExtensions } from 'src/sql-tools/comparers/extension.comparer';
|
||||
import { Reason } from 'src/sql-tools/types';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const testExtension = { name: 'test', synchronize: true };
|
||||
|
||||
describe('compareExtensions', () => {
|
||||
describe('onExtra', () => {
|
||||
it('should work', () => {
|
||||
expect(compareExtensions().onExtra(testExtension)).toEqual([
|
||||
{
|
||||
extensionName: 'test',
|
||||
type: 'ExtensionDrop',
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onMissing', () => {
|
||||
it('should work', () => {
|
||||
expect(compareExtensions().onMissing(testExtension)).toEqual([
|
||||
{
|
||||
type: 'ExtensionCreate',
|
||||
extension: testExtension,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCompare', () => {
|
||||
it('should work', () => {
|
||||
expect(compareExtensions().onCompare(testExtension, testExtension)).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Comparer, DatabaseExtension, Reason } from 'src/sql-tools/types';
|
||||
|
||||
export const compareExtensions = (): Comparer<DatabaseExtension> => ({
|
||||
onMissing: (source) => [
|
||||
{
|
||||
type: 'ExtensionCreate',
|
||||
extension: source,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
],
|
||||
onExtra: (target) => [
|
||||
{
|
||||
type: 'ExtensionDrop',
|
||||
extensionName: target.name,
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
],
|
||||
onCompare: () => {
|
||||
// if the name matches they are the same
|
||||
return [];
|
||||
},
|
||||
});
|
||||
@@ -1,53 +0,0 @@
|
||||
import { compareFunctions } from 'src/sql-tools/comparers/function.comparer';
|
||||
import { DatabaseFunction, Reason } from 'src/sql-tools/types';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const testFunction: DatabaseFunction = {
|
||||
name: 'test',
|
||||
expression: 'CREATE FUNCTION something something something',
|
||||
synchronize: true,
|
||||
};
|
||||
|
||||
describe('compareFunctions', () => {
|
||||
describe('onExtra', () => {
|
||||
it('should work', () => {
|
||||
expect(compareFunctions().onExtra(testFunction)).toEqual([
|
||||
{
|
||||
functionName: 'test',
|
||||
type: 'FunctionDrop',
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onMissing', () => {
|
||||
it('should work', () => {
|
||||
expect(compareFunctions().onMissing(testFunction)).toEqual([
|
||||
{
|
||||
type: 'FunctionCreate',
|
||||
function: testFunction,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCompare', () => {
|
||||
it('should ignore functions with the same hash', () => {
|
||||
expect(compareFunctions().onCompare(testFunction, testFunction)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should report differences if functions have different hashes', () => {
|
||||
const source: DatabaseFunction = { ...testFunction, expression: 'SELECT 1' };
|
||||
const target: DatabaseFunction = { ...testFunction, expression: 'SELECT 2' };
|
||||
expect(compareFunctions().onCompare(source, target)).toEqual([
|
||||
{
|
||||
type: 'FunctionCreate',
|
||||
reason: 'function expression has changed (SELECT 1 vs SELECT 2)',
|
||||
function: source,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Comparer, DatabaseFunction, Reason } from 'src/sql-tools/types';
|
||||
|
||||
export const compareFunctions = (): Comparer<DatabaseFunction> => ({
|
||||
onMissing: (source) => [
|
||||
{
|
||||
type: 'FunctionCreate',
|
||||
function: source,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
],
|
||||
onExtra: (target) => [
|
||||
{
|
||||
type: 'FunctionDrop',
|
||||
functionName: target.name,
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
],
|
||||
onCompare: (source, target) => {
|
||||
if (source.expression !== target.expression) {
|
||||
const reason = `function expression has changed (${source.expression} vs ${target.expression})`;
|
||||
return [
|
||||
{
|
||||
type: 'FunctionCreate',
|
||||
function: source,
|
||||
reason,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
@@ -1,72 +0,0 @@
|
||||
import { compareIndexes } from 'src/sql-tools/comparers/index.comparer';
|
||||
import { DatabaseIndex, Reason } from 'src/sql-tools/types';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const testIndex: DatabaseIndex = {
|
||||
name: 'test',
|
||||
tableName: 'table1',
|
||||
columnNames: ['column1', 'column2'],
|
||||
unique: false,
|
||||
synchronize: true,
|
||||
};
|
||||
|
||||
describe('compareIndexes', () => {
|
||||
describe('onExtra', () => {
|
||||
it('should work', () => {
|
||||
expect(compareIndexes().onExtra(testIndex)).toEqual([
|
||||
{
|
||||
type: 'IndexDrop',
|
||||
indexName: 'test',
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onMissing', () => {
|
||||
it('should work', () => {
|
||||
expect(compareIndexes().onMissing(testIndex)).toEqual([
|
||||
{
|
||||
type: 'IndexCreate',
|
||||
index: testIndex,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCompare', () => {
|
||||
it('should work', () => {
|
||||
expect(compareIndexes().onCompare(testIndex, testIndex)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should drop and recreate when column list is different', () => {
|
||||
const source = {
|
||||
name: 'test',
|
||||
tableName: 'table1',
|
||||
columnNames: ['column1'],
|
||||
unique: true,
|
||||
synchronize: true,
|
||||
};
|
||||
const target = {
|
||||
name: 'test',
|
||||
tableName: 'table1',
|
||||
columnNames: ['column1', 'column2'],
|
||||
unique: true,
|
||||
synchronize: true,
|
||||
};
|
||||
expect(compareIndexes().onCompare(source, target)).toEqual([
|
||||
{
|
||||
indexName: 'test',
|
||||
type: 'IndexDrop',
|
||||
reason: 'columns are different (column1 vs column1,column2)',
|
||||
},
|
||||
{
|
||||
type: 'IndexCreate',
|
||||
index: source,
|
||||
reason: 'columns are different (column1 vs column1,column2)',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,62 +0,0 @@
|
||||
import { asRenameKey, haveEqualColumns } from 'src/sql-tools/helpers';
|
||||
import { Comparer, DatabaseIndex, Reason } from 'src/sql-tools/types';
|
||||
|
||||
export const compareIndexes = (): Comparer<DatabaseIndex> => ({
|
||||
getRenameKey: (index) => {
|
||||
if (index.override) {
|
||||
return index.override.value.sql.replace(index.name, 'INDEX_NAME');
|
||||
}
|
||||
|
||||
return asRenameKey([index.tableName, ...(index.columnNames || []), index.unique]);
|
||||
},
|
||||
onRename: (source, target) => [
|
||||
{
|
||||
type: 'IndexRename',
|
||||
tableName: source.tableName,
|
||||
oldName: target.name,
|
||||
newName: source.name,
|
||||
reason: Reason.Rename,
|
||||
},
|
||||
],
|
||||
onMissing: (source) => [
|
||||
{
|
||||
type: 'IndexCreate',
|
||||
index: source,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
],
|
||||
onExtra: (target) => [
|
||||
{
|
||||
type: 'IndexDrop',
|
||||
indexName: target.name,
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
],
|
||||
onCompare: (source, target) => {
|
||||
const sourceUsing = source.using ?? 'btree';
|
||||
const targetUsing = target.using ?? 'btree';
|
||||
|
||||
let reason = '';
|
||||
|
||||
if (!haveEqualColumns(source.columnNames, target.columnNames)) {
|
||||
reason = `columns are different (${source.columnNames} vs ${target.columnNames})`;
|
||||
} else if (source.unique !== target.unique) {
|
||||
reason = `uniqueness is different (${source.unique} vs ${target.unique})`;
|
||||
} else if (sourceUsing !== targetUsing) {
|
||||
reason = `using method is different (${source.using} vs ${target.using})`;
|
||||
} else if (source.where !== target.where) {
|
||||
reason = `where clause is different (${source.where} vs ${target.where})`;
|
||||
} else if (source.expression !== target.expression) {
|
||||
reason = `expression is different (${source.expression} vs ${target.expression})`;
|
||||
}
|
||||
|
||||
if (reason) {
|
||||
return [
|
||||
{ type: 'IndexDrop', indexName: target.name, reason },
|
||||
{ type: 'IndexCreate', index: source, reason },
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
@@ -1,69 +0,0 @@
|
||||
import { compareOverrides } from 'src/sql-tools/comparers/override.comparer';
|
||||
import { DatabaseOverride, Reason } from 'src/sql-tools/types';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const testOverride: DatabaseOverride = {
|
||||
name: 'test',
|
||||
value: { type: 'function', name: 'test_func', sql: 'func implementation' },
|
||||
synchronize: true,
|
||||
};
|
||||
|
||||
describe('compareOverrides', () => {
|
||||
describe('onExtra', () => {
|
||||
it('should work', () => {
|
||||
expect(compareOverrides().onExtra(testOverride)).toEqual([
|
||||
{
|
||||
type: 'OverrideDrop',
|
||||
overrideName: 'test',
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onMissing', () => {
|
||||
it('should work', () => {
|
||||
expect(compareOverrides().onMissing(testOverride)).toEqual([
|
||||
{
|
||||
type: 'OverrideCreate',
|
||||
override: testOverride,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCompare', () => {
|
||||
it('should work', () => {
|
||||
expect(compareOverrides().onCompare(testOverride, testOverride)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should drop and recreate when the value changes', () => {
|
||||
const source: DatabaseOverride = {
|
||||
name: 'test',
|
||||
value: {
|
||||
type: 'function',
|
||||
name: 'test_func',
|
||||
sql: 'func implementation',
|
||||
},
|
||||
synchronize: true,
|
||||
};
|
||||
const target: DatabaseOverride = {
|
||||
name: 'test',
|
||||
value: {
|
||||
type: 'function',
|
||||
name: 'test_func',
|
||||
sql: 'func implementation2',
|
||||
},
|
||||
synchronize: true,
|
||||
};
|
||||
expect(compareOverrides().onCompare(source, target)).toEqual([
|
||||
{
|
||||
override: source,
|
||||
type: 'OverrideUpdate',
|
||||
reason: expect.stringContaining('value is different'),
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Comparer, DatabaseOverride, Reason } from 'src/sql-tools/types';
|
||||
|
||||
export const compareOverrides = (): Comparer<DatabaseOverride> => ({
|
||||
onMissing: (source) => [
|
||||
{
|
||||
type: 'OverrideCreate',
|
||||
override: source,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
],
|
||||
onExtra: (target) => [
|
||||
{
|
||||
type: 'OverrideDrop',
|
||||
overrideName: target.name,
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
],
|
||||
onCompare: (source, target) => {
|
||||
if (source.value.name !== target.value.name || source.value.sql !== target.value.sql) {
|
||||
const sourceValue = JSON.stringify(source.value);
|
||||
const targetValue = JSON.stringify(target.value);
|
||||
return [
|
||||
{ type: 'OverrideUpdate', override: source, reason: `value is different (${sourceValue} vs ${targetValue})` },
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
import { compareParameters } from 'src/sql-tools/comparers/parameter.comparer';
|
||||
import { DatabaseParameter, Reason } from 'src/sql-tools/types';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const testParameter: DatabaseParameter = {
|
||||
name: 'test',
|
||||
databaseName: 'immich',
|
||||
value: 'on',
|
||||
scope: 'database',
|
||||
synchronize: true,
|
||||
};
|
||||
|
||||
describe('compareParameters', () => {
|
||||
describe('onExtra', () => {
|
||||
it('should work', () => {
|
||||
expect(compareParameters().onExtra(testParameter)).toEqual([
|
||||
{
|
||||
type: 'ParameterReset',
|
||||
databaseName: 'immich',
|
||||
parameterName: 'test',
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onMissing', () => {
|
||||
it('should work', () => {
|
||||
expect(compareParameters().onMissing(testParameter)).toEqual([
|
||||
{
|
||||
type: 'ParameterSet',
|
||||
parameter: testParameter,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCompare', () => {
|
||||
it('should work', () => {
|
||||
expect(compareParameters().onCompare(testParameter, testParameter)).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Comparer, DatabaseParameter, Reason } from 'src/sql-tools/types';
|
||||
|
||||
export const compareParameters = (): Comparer<DatabaseParameter> => ({
|
||||
onMissing: (source) => [
|
||||
{
|
||||
type: 'ParameterSet',
|
||||
parameter: source,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
],
|
||||
onExtra: (target) => [
|
||||
{
|
||||
type: 'ParameterReset',
|
||||
databaseName: target.databaseName,
|
||||
parameterName: target.name,
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
],
|
||||
onCompare: () => {
|
||||
// TODO
|
||||
return [];
|
||||
},
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
import { compareTables } from 'src/sql-tools/comparers/table.comparer';
|
||||
import { DatabaseTable, Reason } from 'src/sql-tools/types';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const testTable: DatabaseTable = {
|
||||
name: 'test',
|
||||
columns: [],
|
||||
constraints: [],
|
||||
indexes: [],
|
||||
triggers: [],
|
||||
synchronize: true,
|
||||
};
|
||||
|
||||
describe('compareParameters', () => {
|
||||
describe('onExtra', () => {
|
||||
it('should work', () => {
|
||||
expect(compareTables({}).onExtra(testTable)).toEqual([
|
||||
{
|
||||
type: 'TableDrop',
|
||||
tableName: 'test',
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onMissing', () => {
|
||||
it('should work', () => {
|
||||
expect(compareTables({}).onMissing(testTable)).toEqual([
|
||||
{
|
||||
type: 'TableCreate',
|
||||
table: testTable,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCompare', () => {
|
||||
it('should work', () => {
|
||||
expect(compareTables({}).onCompare(testTable, testTable)).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
import { compareColumns } from 'src/sql-tools/comparers/column.comparer';
|
||||
import { compareConstraints } from 'src/sql-tools/comparers/constraint.comparer';
|
||||
import { compareIndexes } from 'src/sql-tools/comparers/index.comparer';
|
||||
import { compareTriggers } from 'src/sql-tools/comparers/trigger.comparer';
|
||||
import { compare } from 'src/sql-tools/helpers';
|
||||
import { Comparer, DatabaseTable, Reason, SchemaDiffOptions } from 'src/sql-tools/types';
|
||||
|
||||
export const compareTables = (options: SchemaDiffOptions): Comparer<DatabaseTable> => ({
|
||||
onMissing: (source) => [
|
||||
{
|
||||
type: 'TableCreate',
|
||||
table: source,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
],
|
||||
onExtra: (target) => [
|
||||
{
|
||||
type: 'TableDrop',
|
||||
tableName: target.name,
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
],
|
||||
onCompare: (source, target) => {
|
||||
return [
|
||||
...compare(source.columns, target.columns, options.columns, compareColumns()),
|
||||
...compare(source.indexes, target.indexes, options.indexes, compareIndexes()),
|
||||
...compare(source.constraints, target.constraints, options.constraints, compareConstraints()),
|
||||
...compare(source.triggers, target.triggers, options.triggers, compareTriggers()),
|
||||
];
|
||||
},
|
||||
});
|
||||
@@ -1,88 +0,0 @@
|
||||
import { compareTriggers } from 'src/sql-tools/comparers/trigger.comparer';
|
||||
import { DatabaseTrigger, Reason } from 'src/sql-tools/types';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const testTrigger: DatabaseTrigger = {
|
||||
name: 'test',
|
||||
tableName: 'table1',
|
||||
timing: 'before',
|
||||
actions: ['delete'],
|
||||
scope: 'row',
|
||||
functionName: 'my_trigger_function',
|
||||
synchronize: true,
|
||||
};
|
||||
|
||||
describe('compareTriggers', () => {
|
||||
describe('onExtra', () => {
|
||||
it('should work', () => {
|
||||
expect(compareTriggers().onExtra(testTrigger)).toEqual([
|
||||
{
|
||||
type: 'TriggerDrop',
|
||||
tableName: 'table1',
|
||||
triggerName: 'test',
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onMissing', () => {
|
||||
it('should work', () => {
|
||||
expect(compareTriggers().onMissing(testTrigger)).toEqual([
|
||||
{
|
||||
type: 'TriggerCreate',
|
||||
trigger: testTrigger,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCompare', () => {
|
||||
it('should work', () => {
|
||||
expect(compareTriggers().onCompare(testTrigger, testTrigger)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should detect a change in function name', () => {
|
||||
const source: DatabaseTrigger = { ...testTrigger, functionName: 'my_new_name' };
|
||||
const target: DatabaseTrigger = { ...testTrigger, functionName: 'my_old_name' };
|
||||
const reason = `function is different (my_new_name vs my_old_name)`;
|
||||
expect(compareTriggers().onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]);
|
||||
});
|
||||
|
||||
it('should detect a change in actions', () => {
|
||||
const source: DatabaseTrigger = { ...testTrigger, actions: ['delete'] };
|
||||
const target: DatabaseTrigger = { ...testTrigger, actions: ['delete', 'insert'] };
|
||||
const reason = `action is different (delete vs delete,insert)`;
|
||||
expect(compareTriggers().onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]);
|
||||
});
|
||||
|
||||
it('should detect a change in timing', () => {
|
||||
const source: DatabaseTrigger = { ...testTrigger, timing: 'before' };
|
||||
const target: DatabaseTrigger = { ...testTrigger, timing: 'after' };
|
||||
const reason = `timing method is different (before vs after)`;
|
||||
expect(compareTriggers().onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]);
|
||||
});
|
||||
|
||||
it('should detect a change in scope', () => {
|
||||
const source: DatabaseTrigger = { ...testTrigger, scope: 'row' };
|
||||
const target: DatabaseTrigger = { ...testTrigger, scope: 'statement' };
|
||||
const reason = `scope is different (row vs statement)`;
|
||||
expect(compareTriggers().onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]);
|
||||
});
|
||||
|
||||
it('should detect a change in new table reference', () => {
|
||||
const source: DatabaseTrigger = { ...testTrigger, referencingNewTableAs: 'new_table' };
|
||||
const target: DatabaseTrigger = { ...testTrigger, referencingNewTableAs: undefined };
|
||||
const reason = `new table reference is different (new_table vs undefined)`;
|
||||
expect(compareTriggers().onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]);
|
||||
});
|
||||
|
||||
it('should detect a change in old table reference', () => {
|
||||
const source: DatabaseTrigger = { ...testTrigger, referencingOldTableAs: 'old_table' };
|
||||
const target: DatabaseTrigger = { ...testTrigger, referencingOldTableAs: undefined };
|
||||
const reason = `old table reference is different (old_table vs undefined)`;
|
||||
expect(compareTriggers().onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
import { Comparer, DatabaseTrigger, Reason } from 'src/sql-tools/types';
|
||||
|
||||
export const compareTriggers = (): Comparer<DatabaseTrigger> => ({
|
||||
onMissing: (source) => [
|
||||
{
|
||||
type: 'TriggerCreate',
|
||||
trigger: source,
|
||||
reason: Reason.MissingInTarget,
|
||||
},
|
||||
],
|
||||
onExtra: (target) => [
|
||||
{
|
||||
type: 'TriggerDrop',
|
||||
tableName: target.tableName,
|
||||
triggerName: target.name,
|
||||
reason: Reason.MissingInSource,
|
||||
},
|
||||
],
|
||||
onCompare: (source, target) => {
|
||||
let reason = '';
|
||||
if (source.functionName !== target.functionName) {
|
||||
reason = `function is different (${source.functionName} vs ${target.functionName})`;
|
||||
} else if (source.actions.join(' OR ') !== target.actions.join(' OR ')) {
|
||||
reason = `action is different (${source.actions} vs ${target.actions})`;
|
||||
} else if (source.timing !== target.timing) {
|
||||
reason = `timing method is different (${source.timing} vs ${target.timing})`;
|
||||
} else if (source.scope !== target.scope) {
|
||||
reason = `scope is different (${source.scope} vs ${target.scope})`;
|
||||
} else if (source.referencingNewTableAs !== target.referencingNewTableAs) {
|
||||
reason = `new table reference is different (${source.referencingNewTableAs} vs ${target.referencingNewTableAs})`;
|
||||
} else if (source.referencingOldTableAs !== target.referencingOldTableAs) {
|
||||
reason = `old table reference is different (${source.referencingOldTableAs} vs ${target.referencingOldTableAs})`;
|
||||
}
|
||||
|
||||
if (reason) {
|
||||
return [{ type: 'TriggerCreate', trigger: source, reason }];
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
@@ -1,104 +0,0 @@
|
||||
import { DefaultNamingStrategy } from 'src/sql-tools/naming/default.naming';
|
||||
import { HashNamingStrategy } from 'src/sql-tools/naming/hash.naming';
|
||||
import { NamingInterface, NamingItem } from 'src/sql-tools/naming/naming.interface';
|
||||
import {
|
||||
BaseContextOptions,
|
||||
DatabaseEnum,
|
||||
DatabaseExtension,
|
||||
DatabaseFunction,
|
||||
DatabaseOverride,
|
||||
DatabaseParameter,
|
||||
DatabaseSchema,
|
||||
DatabaseTable,
|
||||
} from 'src/sql-tools/types';
|
||||
|
||||
const asOverrideKey = (type: string, name: string) => `${type}:${name}`;
|
||||
|
||||
const isNamingInterface = (strategy: any): strategy is NamingInterface => {
|
||||
return typeof strategy === 'object' && typeof strategy.getName === 'function';
|
||||
};
|
||||
|
||||
const asNamingStrategy = (strategy: 'hash' | 'default' | NamingInterface): NamingInterface => {
|
||||
if (isNamingInterface(strategy)) {
|
||||
return strategy;
|
||||
}
|
||||
|
||||
switch (strategy) {
|
||||
case 'hash': {
|
||||
return new HashNamingStrategy();
|
||||
}
|
||||
|
||||
default: {
|
||||
return new DefaultNamingStrategy();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export class BaseContext {
|
||||
databaseName: string;
|
||||
schemaName: string;
|
||||
overrideTableName: string;
|
||||
|
||||
tables: DatabaseTable[] = [];
|
||||
functions: DatabaseFunction[] = [];
|
||||
enums: DatabaseEnum[] = [];
|
||||
extensions: DatabaseExtension[] = [];
|
||||
parameters: DatabaseParameter[] = [];
|
||||
overrides: DatabaseOverride[] = [];
|
||||
warnings: string[] = [];
|
||||
|
||||
private namingStrategy: NamingInterface;
|
||||
|
||||
constructor(options: BaseContextOptions) {
|
||||
this.databaseName = options.databaseName ?? 'postgres';
|
||||
this.schemaName = options.schemaName ?? 'public';
|
||||
this.overrideTableName = options.overrideTableName ?? 'migration_overrides';
|
||||
this.namingStrategy = asNamingStrategy(options.namingStrategy ?? 'hash');
|
||||
}
|
||||
|
||||
getNameFor(item: NamingItem) {
|
||||
return this.namingStrategy.getName(item);
|
||||
}
|
||||
|
||||
getTableByName(name: string) {
|
||||
return this.tables.find((table) => table.name === name);
|
||||
}
|
||||
|
||||
warn(context: string, message: string) {
|
||||
this.warnings.push(`[${context}] ${message}`);
|
||||
}
|
||||
|
||||
build(): DatabaseSchema {
|
||||
const overrideMap = new Map<string, DatabaseOverride>();
|
||||
for (const override of this.overrides) {
|
||||
const { type, name } = override.value;
|
||||
overrideMap.set(asOverrideKey(type, name), override);
|
||||
}
|
||||
|
||||
for (const func of this.functions) {
|
||||
func.override = overrideMap.get(asOverrideKey('function', func.name));
|
||||
}
|
||||
|
||||
for (const { indexes, triggers } of this.tables) {
|
||||
for (const index of indexes) {
|
||||
index.override = overrideMap.get(asOverrideKey('index', index.name));
|
||||
}
|
||||
|
||||
for (const trigger of triggers) {
|
||||
trigger.override = overrideMap.get(asOverrideKey('trigger', trigger.name));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
databaseName: this.databaseName,
|
||||
schemaName: this.schemaName,
|
||||
tables: this.tables,
|
||||
functions: this.functions,
|
||||
enums: this.enums,
|
||||
extensions: this.extensions,
|
||||
parameters: this.parameters,
|
||||
overrides: this.overrides,
|
||||
warnings: this.warnings,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
||||
import { BaseContext } from 'src/sql-tools/contexts/base-context';
|
||||
import { ColumnOptions } from 'src/sql-tools/decorators/column.decorator';
|
||||
import { TableOptions } from 'src/sql-tools/decorators/table.decorator';
|
||||
import { DatabaseColumn, DatabaseTable, SchemaFromCodeOptions } from 'src/sql-tools/types';
|
||||
|
||||
type TableMetadata = { options: TableOptions; object: Function; methodToColumn: Map<string | symbol, DatabaseColumn> };
|
||||
|
||||
export class ProcessorContext extends BaseContext {
|
||||
constructor(public options: SchemaFromCodeOptions) {
|
||||
options.createForeignKeyIndexes = options.createForeignKeyIndexes ?? true;
|
||||
options.overrides = options.overrides ?? false;
|
||||
super(options);
|
||||
}
|
||||
|
||||
classToTable: WeakMap<Function, DatabaseTable> = new WeakMap();
|
||||
tableToMetadata: WeakMap<DatabaseTable, TableMetadata> = new WeakMap();
|
||||
|
||||
getTableByObject(object: Function) {
|
||||
return this.classToTable.get(object);
|
||||
}
|
||||
|
||||
getTableMetadata(table: DatabaseTable) {
|
||||
const metadata = this.tableToMetadata.get(table);
|
||||
if (!metadata) {
|
||||
throw new Error(`Table metadata not found for table: ${table.name}`);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
addTable(table: DatabaseTable, options: TableOptions, object: Function) {
|
||||
this.tables.push(table);
|
||||
this.classToTable.set(object, table);
|
||||
this.tableToMetadata.set(table, { options, object, methodToColumn: new Map() });
|
||||
}
|
||||
|
||||
getColumnByObjectAndPropertyName(
|
||||
object: object,
|
||||
propertyName: string | symbol,
|
||||
): { table?: DatabaseTable; column?: DatabaseColumn } {
|
||||
const table = this.getTableByObject(object.constructor);
|
||||
if (!table) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const tableMetadata = this.tableToMetadata.get(table);
|
||||
if (!tableMetadata) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const column = tableMetadata.methodToColumn.get(propertyName);
|
||||
|
||||
return { table, column };
|
||||
}
|
||||
|
||||
addColumn(table: DatabaseTable, column: DatabaseColumn, options: ColumnOptions, propertyName: string | symbol) {
|
||||
table.columns.push(column);
|
||||
const tableMetadata = this.getTableMetadata(table);
|
||||
tableMetadata.methodToColumn.set(propertyName, column);
|
||||
}
|
||||
|
||||
warnMissingTable(context: string, object: object, propertyName?: symbol | string) {
|
||||
const label = object.constructor.name + (propertyName ? '.' + String(propertyName) : '');
|
||||
this.warn(context, `Unable to find table (${label})`);
|
||||
}
|
||||
|
||||
warnMissingColumn(context: string, object: object, propertyName?: symbol | string) {
|
||||
const label = object.constructor.name + (propertyName ? '.' + String(propertyName) : '');
|
||||
this.warn(context, `Unable to find column (${label})`);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { BaseContext } from 'src/sql-tools/contexts/base-context';
|
||||
import { SchemaFromDatabaseOptions } from 'src/sql-tools/types';
|
||||
|
||||
export class ReaderContext extends BaseContext {
|
||||
constructor(public options: SchemaFromDatabaseOptions) {
|
||||
super(options);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { TriggerFunction, TriggerFunctionOptions } from 'src/sql-tools/decorators/trigger-function.decorator';
|
||||
|
||||
export const AfterDeleteTrigger = (options: Omit<TriggerFunctionOptions, 'timing' | 'actions'>) =>
|
||||
TriggerFunction({
|
||||
timing: 'after',
|
||||
actions: ['delete'],
|
||||
...options,
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
import { TriggerFunction, TriggerFunctionOptions } from 'src/sql-tools/decorators/trigger-function.decorator';
|
||||
|
||||
export const AfterInsertTrigger = (options: Omit<TriggerFunctionOptions, 'timing' | 'actions'>) =>
|
||||
TriggerFunction({
|
||||
timing: 'after',
|
||||
actions: ['insert'],
|
||||
...options,
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
import { TriggerFunction, TriggerFunctionOptions } from 'src/sql-tools/decorators/trigger-function.decorator';
|
||||
|
||||
export const BeforeUpdateTrigger = (options: Omit<TriggerFunctionOptions, 'timing' | 'actions'>) =>
|
||||
TriggerFunction({
|
||||
timing: 'before',
|
||||
actions: ['update'],
|
||||
...options,
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
import { register } from 'src/sql-tools/register';
|
||||
|
||||
export type CheckOptions = {
|
||||
name?: string;
|
||||
expression: string;
|
||||
synchronize?: boolean;
|
||||
};
|
||||
export const Check = (options: CheckOptions): ClassDecorator => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
return (object: Function) => void register({ type: 'checkConstraint', item: { object, options } });
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
import { asOptions } from 'src/sql-tools/helpers';
|
||||
import { register } from 'src/sql-tools/register';
|
||||
import { ColumnStorage, ColumnType, DatabaseEnum } from 'src/sql-tools/types';
|
||||
|
||||
export type ColumnValue = null | boolean | string | number | Array<unknown> | object | Date | (() => string);
|
||||
|
||||
export type ColumnBaseOptions = {
|
||||
name?: string;
|
||||
primary?: boolean;
|
||||
type?: ColumnType;
|
||||
nullable?: boolean;
|
||||
length?: number;
|
||||
default?: ColumnValue;
|
||||
comment?: string;
|
||||
synchronize?: boolean;
|
||||
storage?: ColumnStorage;
|
||||
identity?: boolean;
|
||||
index?: boolean;
|
||||
indexName?: string;
|
||||
unique?: boolean;
|
||||
uniqueConstraintName?: string;
|
||||
};
|
||||
|
||||
export type ColumnOptions = ColumnBaseOptions & {
|
||||
enum?: DatabaseEnum;
|
||||
array?: boolean;
|
||||
};
|
||||
|
||||
export const Column = (options: string | ColumnOptions = {}): PropertyDecorator => {
|
||||
return (object: object, propertyName: string | symbol) =>
|
||||
void register({ type: 'column', item: { object, propertyName, options: asOptions(options) } });
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ColumnValue } from 'src/sql-tools/decorators/column.decorator';
|
||||
import { register } from 'src/sql-tools/register';
|
||||
import { ParameterScope } from 'src/sql-tools/types';
|
||||
|
||||
export type ConfigurationParameterOptions = {
|
||||
name: string;
|
||||
value: ColumnValue;
|
||||
scope: ParameterScope;
|
||||
synchronize?: boolean;
|
||||
};
|
||||
export const ConfigurationParameter = (options: ConfigurationParameterOptions): ClassDecorator => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
return (object: Function) => void register({ type: 'configurationParameter', item: { object, options } });
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Column, ColumnOptions } from 'src/sql-tools/decorators/column.decorator';
|
||||
|
||||
export const CreateDateColumn = (options: ColumnOptions = {}): PropertyDecorator => {
|
||||
return Column({
|
||||
type: 'timestamp with time zone',
|
||||
default: () => 'now()',
|
||||
...options,
|
||||
});
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
import { register } from 'src/sql-tools/register';
|
||||
|
||||
export type DatabaseOptions = {
|
||||
name?: string;
|
||||
synchronize?: boolean;
|
||||
};
|
||||
export const Database = (options: DatabaseOptions): ClassDecorator => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
return (object: Function) => void register({ type: 'database', item: { object, options } });
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Column, ColumnOptions } from 'src/sql-tools/decorators/column.decorator';
|
||||
|
||||
export const DeleteDateColumn = (options: ColumnOptions = {}): PropertyDecorator => {
|
||||
return Column({
|
||||
type: 'timestamp with time zone',
|
||||
nullable: true,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
import { asOptions } from 'src/sql-tools/helpers';
|
||||
import { register } from 'src/sql-tools/register';
|
||||
|
||||
export type ExtensionOptions = {
|
||||
name: string;
|
||||
synchronize?: boolean;
|
||||
};
|
||||
export const Extension = (options: string | ExtensionOptions): ClassDecorator => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
return (object: Function) => void register({ type: 'extension', item: { object, options: asOptions(options) } });
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user