import { ISaveOptions, ISaveResult, OrphanPolicy } from './interfaces.js'; import { ISortedPlan } from './subject-sorter.js'; import { IJunctionDelta, IOrphanDelta, Subject } from './subject.js'; /** Rows per batched statement when the caller does not say. */ export declare const DEFAULT_SAVE_CHUNK = 100; /** * Runs a sorted plan. * * Every statement is built through `createQuery`, so table naming, schema qualification and * identifier escaping are the same as on the ActiveRecord paths. No connection is threaded by * hand: `OrmDriver.transaction()` carries it in `AsyncLocalStorage` and the builders resolved * from that driver pick it up automatically. * * The executor does **not** open the transaction — `UnitOfWork` does — so that a caller can * drive it directly in a test and so that nested saves inside one transaction fold into * savepoints rather than each opening their own. */ export declare class SubjectExecutor { protected options: ISaveOptions; constructor(options: ISaveOptions); execute(plan: ISortedPlan): Promise; /** * Inserts every insert subject in the order the sorter produced, reading each generated key * back before moving on so the next subject's foreign keys can be resolved. * * One statement per row, deliberately, and NOT subject to `options.chunk`: a batched * multi-row INSERT can only return keys where the dialect supports RETURNING or where * `insertIdIsFirstOfBatch` holds, and a subject's key is needed by the very next subject in * the order. Batching here is a real feature — it has to carry the per-dialect key-backfill * rules with it — not something to fold into the chunking used for junction and orphan rows. */ protected runInserts(plan: ISortedPlan, result: ISaveResult): Promise; /** * One INSERT, with the key read back. * * Mirrors `ModelBase.insert()`'s key handling rather than inventing a second one: client-side * keys are generated first, `assigned` keys are asserted before anything touches the database, * and RETURNING is requested when an `auto` key needs to come back and the dialect supports it. */ protected insertOne(subject: Subject): Promise; /** * Runs every update, including the follow-up updates that carry deferred self-referencing * foreign keys. * * A subject whose payload comes out empty emits nothing. This is the single place that * decides whether a row actually changed: pending foreign keys are written onto the model * first and `changeSet()` is read afterwards, so a re-parented child that was clean * when the subjects were built is caught here and nowhere else. */ protected runUpdates(plan: ISortedPlan, result: ISaveResult): Promise; /** * The column payload for one UPDATE, or `null` when there is nothing to write. * * The primary key columns are excluded: writing them is a no-op at best and, for a model * whose key column differs from its snapshot for any other reason, a silent identity change. * * @param subject - an update subject, or an insert subject with deferred foreign keys */ protected updatePayload(subject: Subject): Record | null; /** * The column payload for one INSERT. * * Starts from the model's own serialization, drops every column whose foreign key is * deferred to a follow-up UPDATE, then writes every pending foreign key from its target's * now-known join column. The overwrite matters: `StandardModelToSqlConverter` already wrote * that column from the relation object, and for a target inserted moments ago that value was * `undefined` when the model was serialized. */ protected insertPayload(subject: Subject): Record; /** * Writes a generated key onto the model. * * Uses `setPkValue` — the primary-keys helper that assigns the key columns and nothing else — * rather than the `PrimaryKeyValue` setter, whose `RelationType.One` branch also writes the * new key onto the owner's `SingleRelation` wrapper, which persists nothing. The executor * resolves foreign keys itself and does not want that side effect. * * A key the caller already supplied ( uuid / assigned strategies ) is never overwritten. */ protected backfillKey(subject: Subject, returning: any[], lastInsertId: number, needsKeyBack: boolean): void; /** * Creates and destroys junction rows. * * Rows are written column-first rather than through the junction model, so a junction model * is not required to declare `@BelongsTo` on both sides — `ManyToManyRelationList.update()` * does require that, and none of the existing junction fixtures satisfy it. * * Inserts run before deletes so that re-linking the same pair inside one save cannot * momentarily violate a unique constraint on the junction table in the other order. */ protected runJunctions(plan: ISortedPlan, result: ISaveResult): Promise; protected insertJunctionRows(delta: IJunctionDelta, result: ISaveResult): Promise; protected deleteJunctionRows(delta: IJunctionDelta, result: ISaveResult): Promise; /** * Applies the orphan policy to every detached row. * * `nullify` and `soft-delete` run first as UPDATEs, then `delete` runs as DELETEs, and the * deltas arrive from the sorter already ordered children-before-parents so a delete cannot * strand a foreign key. * * `createQuery` only adds the default `DeletedAt IS NULL` filter to a SelectQueryBuilder, so * these builders are unfiltered — which is what stamping an already-soft-deleted row needs. */ protected runOrphans(plan: ISortedPlan, result: ISaveResult): Promise; /** * The policy actually applied to `delta`. * * `delete` on a model that declares `@SoftDelete` degrades to `soft-delete`, so orphaning a * row and calling `destroy()` on it mean the same thing. `ModelBase.destroy()` has always * stamped `DeletedAt` rather than issuing a DELETE for such a model; an orphan taking the * other branch made "delete this row" depend on which code path reached it, and hard-erased * rows the model had declared should never be hard-erased. */ protected effectivePolicy(delta: IOrphanDelta): OrphanPolicy; protected updateOrphans(delta: IOrphanDelta, result: ISaveResult): Promise; protected deleteOrphans(delta: IOrphanDelta, result: ISaveResult): Promise; /** Rows per batched statement. */ protected get chunkSize(): number; /** Splits `items` into runs of at most `chunkSize`. */ protected chunked(items: T[]): T[][]; } //# sourceMappingURL=subject-executor.d.ts.map