Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 55 additions & 8 deletions packages/@n8n/db/src/entities/execution-data.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import { Column, Entity, JoinColumn, OneToOne, PrimaryColumn } from '@n8n/typeorm';
import { IWorkflowBase } from 'n8n-workflow';
import {
BeforeInsert,
BeforeUpdate,
Column,
Entity,
JoinColumn,
ManyToOne,
OneToOne,
PrimaryColumn,
Relation,
} from '@n8n/typeorm';
import { IWorkflowBase, UnexpectedError } from 'n8n-workflow';

import { JsonColumn } from './abstract-entity';
import { ExecutionEntity } from './execution-entity';
import { ISimplifiedPinData } from './types-db';
import { WorkflowHistory } from './workflow-history';
import { idStringifier } from '../utils/transformers';

type WorkflowData = Omit<IWorkflowBase, 'pinData'> & { pinData?: ISimplifiedPinData };

@Entity()
export class ExecutionData {
@Column('text')
Expand All @@ -21,17 +34,51 @@ export class ExecutionData {
* due to `INodeExecutionData`, so we use a simplified version so `QueryDeepPartialEntity`
* can resolve and calls to `update`, `insert`, and `insert` pass typechecking.
*/
@JsonColumn()
workflowData: Omit<IWorkflowBase, 'pinData'> & { pinData?: ISimplifiedPinData };
@JsonColumn({ nullable: true })
workflowData: WorkflowData | null;

@Column({ type: 'varchar', length: 36, nullable: true })
workflowVersionId: string | null;

@ManyToOne(
() => WorkflowHistory,
(wh) => wh.versionId,
{ onDelete: 'SET NULL', nullable: true },
)
workflowHistory: (Omit<WorkflowHistory, 'workflow'> & { workflow: WorkflowData }) | null;

@BeforeInsert()
@BeforeUpdate()
validateRelations() {
if (this.workflowData === null && this.workflowVersionId === null) {
throw new Error('Either workflowData or workflowVersionId must be provided');
}
}

get workflow(): WorkflowData {
if (this.workflowData) {
return this.workflowData;
}

if (this.workflowHistory === null) {
throw new UnexpectedError('ExecutionData invariant broken');
}

return { ...this.workflowHistory.workflow, ...this.workflowHistory };
}

@PrimaryColumn({ transformer: idStringifier })
executionId: string;

@OneToOne('ExecutionEntity', 'executionData', {
onDelete: 'CASCADE',
})
@OneToOne(
() => ExecutionEntity,
(ee) => ee.executionData,
{
onDelete: 'CASCADE',
},
)
@JoinColumn({
name: 'executionId',
})
execution: ExecutionEntity;
execution: Relation<ExecutionEntity>;
}
9 changes: 6 additions & 3 deletions packages/@n8n/db/src/entities/shared-workflow.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WorkflowSharingRole } from '@n8n/permissions';
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import { Column, Entity, ManyToOne, PrimaryColumn, Relation } from '@n8n/typeorm';

import { WithTimestamps } from './abstract-entity';
import { Project } from './project';
Expand All @@ -10,8 +10,11 @@ export class SharedWorkflow extends WithTimestamps {
@Column({ type: 'varchar' })
role: WorkflowSharingRole;

@ManyToOne('WorkflowEntity', 'shared')
workflow: WorkflowEntity;
@ManyToOne(
() => WorkflowEntity,
(we) => we.shared,
)
workflow: Relation<WorkflowEntity>;

@PrimaryColumn()
workflowId: string;
Expand Down
11 changes: 6 additions & 5 deletions packages/@n8n/db/src/entities/workflow-entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ManyToMany,
ManyToOne,
OneToMany,
Relation,
} from '@n8n/typeorm';
import { Length } from 'class-validator';
import { IConnections, IDataObject, IWorkflowSettings, WorkflowFEMeta } from 'n8n-workflow';
Expand All @@ -15,11 +16,11 @@ import type { INode } from 'n8n-workflow';
import { JsonColumn, WithTimestampsAndStringId, dbType } from './abstract-entity';
import { type Folder } from './folder';
import type { SharedWorkflow } from './shared-workflow';
import type { TagEntity } from './tag-entity';
import { TagEntity } from './tag-entity';
import type { TestRun } from './test-run.ee';
import type { ISimplifiedPinData, IWorkflowDb } from './types-db';
import type { WorkflowStatistics } from './workflow-statistics';
import type { WorkflowTagMapping } from './workflow-tag-mapping';
import { WorkflowTagMapping } from './workflow-tag-mapping';
import { objectRetriever, sqlite } from '../utils/transformers';

@Entity()
Expand Down Expand Up @@ -69,7 +70,7 @@ export class WorkflowEntity extends WithTimestampsAndStringId implements IWorkfl
})
meta?: WorkflowFEMeta;

@ManyToMany('TagEntity', 'workflows')
@ManyToMany(() => TagEntity, 'workflows')
@JoinTable({
name: 'workflows_tags', // table name for the junction table of this relation
joinColumn: {
Expand All @@ -83,11 +84,11 @@ export class WorkflowEntity extends WithTimestampsAndStringId implements IWorkfl
})
tags?: TagEntity[];

@OneToMany('WorkflowTagMapping', 'workflows')
@OneToMany(() => WorkflowTagMapping, 'workflows')
tagMappings: WorkflowTagMapping[];

@OneToMany('SharedWorkflow', 'workflow')
shared: SharedWorkflow[];
shared: Relation<SharedWorkflow[]>;

@OneToMany('WorkflowStatistics', 'workflow')
@JoinColumn({ referencedColumnName: 'workflow' })
Expand Down
10 changes: 7 additions & 3 deletions packages/@n8n/db/src/entities/workflow-history.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import { Column, Entity, ManyToOne, OneToMany, PrimaryColumn, Relation } from '@n8n/typeorm';
import { IConnections } from 'n8n-workflow';
import type { INode } from 'n8n-workflow';

import { JsonColumn, WithTimestamps } from './abstract-entity';
import type { ExecutionData } from './execution-data';
import { WorkflowEntity } from './workflow-entity';

@Entity()
Expand All @@ -22,8 +23,11 @@ export class WorkflowHistory extends WithTimestamps {
@Column()
authors: string;

@ManyToOne('WorkflowEntity', {
@ManyToOne(() => WorkflowEntity, {
onDelete: 'CASCADE',
})
workflow: WorkflowEntity;
workflow: Relation<WorkflowEntity>;

@OneToMany('ExecutionData', 'workflowVersionId')
executionData: Relation<ExecutionData[]>;
}
4 changes: 2 additions & 2 deletions packages/@n8n/db/src/entities/workflow-statistics.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';

import { DateTimeColumn } from './abstract-entity';
import { StatisticsNames } from './types-db';
import type { StatisticsNames } from './types-db';
import { WorkflowEntity } from './workflow-entity';

@Entity()
Expand All @@ -18,7 +18,7 @@ export class WorkflowStatistics {
@PrimaryColumn({ length: 128 })
name: StatisticsNames;

@ManyToOne('WorkflowEntity', 'shared')
@ManyToOne(() => WorkflowEntity, 'shared')
workflow: WorkflowEntity;

@PrimaryColumn()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { MigrationContext, ReversibleMigration } from '../migration-types';

export class AddWorkflowVersionIdToExecutionData1762858574621 implements ReversibleMigration {
async up({ schemaBuilder: { addColumns, column } }: MigrationContext) {
await addColumns('execution_data', [column('workflowVersionId').varchar()]);
}

async down({ schemaBuilder: { dropColumns } }: MigrationContext) {
await dropColumns('execution_data', ['workflowVersionId']);
}
}
2 changes: 2 additions & 0 deletions packages/@n8n/db/src/migrations/mysqldb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ import { CreateOAuthEntities1760116750277 } from '../common/1760116750277-Create
import { CreateWorkflowDependencyTable1760314000000 } from '../common/1760314000000-CreateWorkflowDependencyTable';
import { AddWorkflowDescriptionColumn1762177736257 } from '../common/1762177736257-AddWorkflowDescriptionColumn';
import { BackfillMissingWorkflowHistoryRecords1762763704614 } from '../common/1762763704614-BackfillMissingWorkflowHistoryRecords';
import { AddWorkflowVersionIdToExecutionData1762858574621 } from '../common/1762858574621-AddWorkflowVersionIdToExecutionData';
import type { Migration } from '../migration-types';

export const mysqlMigrations: Migration[] = [
Expand Down Expand Up @@ -229,4 +230,5 @@ export const mysqlMigrations: Migration[] = [
CreateOAuthEntities1760116750277,
BackfillMissingWorkflowHistoryRecords1762763704614,
AddWorkflowHistoryAutoSaveFields1762847206508,
AddWorkflowVersionIdToExecutionData1762858574621,
];
2 changes: 2 additions & 0 deletions packages/@n8n/db/src/migrations/postgresdb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ import { DropUnusedChatHubColumns1760965142113 } from '../common/1760965142113-D
import { AddWorkflowDescriptionColumn1762177736257 } from '../common/1762177736257-AddWorkflowDescriptionColumn';
import { BackfillMissingWorkflowHistoryRecords1762763704614 } from '../common/1762763704614-BackfillMissingWorkflowHistoryRecords';
import { AddWorkflowHistoryAutoSaveFields1762847206508 } from '../common/1762847206508-AddWorkflowHistoryAutoSaveFields';
import { AddWorkflowVersionIdToExecutionData1762858574621 } from '../common/1762858574621-AddWorkflowVersionIdToExecutionData';
import type { Migration } from '../migration-types';
import { ChangeDefaultForIdInUserTable1762771264000 } from './1762771264000-ChangeDefaultForIdInUserTable';

Expand Down Expand Up @@ -229,4 +230,5 @@ export const postgresMigrations: Migration[] = [
BackfillMissingWorkflowHistoryRecords1762763704614,
ChangeDefaultForIdInUserTable1762771264000,
AddWorkflowHistoryAutoSaveFields1762847206508,
AddWorkflowVersionIdToExecutionData1762858574621,
];
2 changes: 2 additions & 0 deletions packages/@n8n/db/src/migrations/sqlite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ import { DropUnusedChatHubColumns1760965142113 } from '../common/1760965142113-D
import { AddWorkflowDescriptionColumn1762177736257 } from '../common/1762177736257-AddWorkflowDescriptionColumn';
import { BackfillMissingWorkflowHistoryRecords1762763704614 } from '../common/1762763704614-BackfillMissingWorkflowHistoryRecords';
import { AddWorkflowHistoryAutoSaveFields1762847206508 } from '../common/1762847206508-AddWorkflowHistoryAutoSaveFields';
import { AddWorkflowVersionIdToExecutionData1762858574621 } from '../common/1762858574621-AddWorkflowVersionIdToExecutionData';
import type { Migration } from '../migration-types';

const sqliteMigrations: Migration[] = [
Expand Down Expand Up @@ -221,6 +222,7 @@ const sqliteMigrations: Migration[] = [
CreateOAuthEntities1760116750277,
BackfillMissingWorkflowHistoryRecords1762763704614,
AddWorkflowHistoryAutoSaveFields1762847206508,
AddWorkflowVersionIdToExecutionData1762858574621,
];

export { sqliteMigrations };
15 changes: 12 additions & 3 deletions packages/@n8n/db/src/repositories/execution-data.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Service } from '@n8n/di';
import { DataSource, In, Repository } from '@n8n/typeorm';
import type { EntityManager } from '@n8n/typeorm';
import type { QueryDeepPartialEntity } from '@n8n/typeorm/query-builder/QueryPartialEntity';
import { IWorkflowBase } from 'n8n-workflow';

import { ISimplifiedPinData } from 'entities/types-db';

import { ExecutionData } from '../entities';

Expand All @@ -18,12 +21,18 @@ export class ExecutionDataRepository extends Repository<ExecutionData> {
return await transactionManager.insert(ExecutionData, data);
}

async findByExecutionIds(executionIds: string[]) {
async findByExecutionIds(executionIds: string[]): Promise<
Array<
Omit<IWorkflowBase, 'pinData'> & {
pinData?: ISimplifiedPinData;
}
>
> {
return await this.find({
select: ['workflowData'],
select: ['workflowData', 'workflowHistory'],
where: {
executionId: In(executionIds),
},
}).then((executionData) => executionData.map(({ workflowData }) => workflowData));
}).then((executionData) => executionData.map(({ workflow }) => workflow));
}
}
11 changes: 8 additions & 3 deletions packages/@n8n/db/src/repositories/execution.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
this.errorReporter.error('Found successful execution where data is empty stringified array', {
extra: {
executionId: execution.id,
workflowId: executionData?.workflowData.id,
workflowId: executionData?.workflow.id,
},
});
}
Expand Down Expand Up @@ -364,7 +364,12 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
// In the non-pooling sqlite driver we can't use transactions, because that creates nested transactions under highly concurrent loads, leading to errors in the database
const { identifiers: inserted } = await this.insert({ ...rest, createdAt: new Date() });
const { id: executionId } = inserted[0] as { id: string };
await this.executionDataRepository.insert({ executionId, workflowData, data });
await this.executionDataRepository.insert({
executionId,
workflowData,
data,
workflowVersionId: currentWorkflow.versionId,
});
return String(executionId);
} else {
// All other database drivers should create executions and execution-data atomically
Expand All @@ -375,7 +380,7 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
});
const { id: executionId } = inserted[0] as { id: string };
await this.executionDataRepository.createExecutionDataForExecution(
{ executionId, workflowData, data },
{ executionId, workflowData, data, workflowVersionId: currentWorkflow.versionId },
transactionManager,
);
return String(executionId);
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/modules/chat-hub/chat-hub-agent.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class ChatHubAgent extends WithTimestamps {
/**
* The selected credential to use by default with the selected LLM provider (if applicable).
*/
@ManyToOne('CredentialsEntity', { onDelete: 'SET NULL', nullable: true })
@ManyToOne(() => CredentialsEntity, { onDelete: 'SET NULL', nullable: true })
@JoinColumn({ name: 'credentialId' })
credential?: CredentialsEntity | null;

Expand Down
Loading
Loading