diff --git a/integration-tests/__tests__/data-duplicator.spec.js b/integration-tests/__tests__/data-duplicator.spec.js index 7d5c532e5..81444830a 100644 --- a/integration-tests/__tests__/data-duplicator.spec.js +++ b/integration-tests/__tests__/data-duplicator.spec.js @@ -91,4 +91,65 @@ describe('Data duplicator', () => { expect(res2.rows[0].cnt.toString()).toEqual('6'); }) ); + + test.each(engines.filter(x => !x.skipDataDuplicator).map(engine => [engine.label, engine]))( + 'Skip nullable weak refs - %s', + testWrapper(async (conn, driver, engine) => { + runCommandOnDriver(conn, driver, dmp => + dmp.createTable({ + pureName: 't1', + columns: [ + { columnName: 'id', dataType: 'int', notNull: true }, + { columnName: 'val', dataType: 'varchar(50)' }, + ], + primaryKey: { + columns: [{ columnName: 'id' }], + }, + }) + ); + runCommandOnDriver(conn, driver, dmp => + dmp.createTable({ + pureName: 't2', + columns: [ + { columnName: 'id', dataType: 'int', autoIncrement: true, notNull: true }, + { columnName: 'val', dataType: 'varchar(50)' }, + { columnName: 'valfk', dataType: 'int', notNull: false }, + ], + primaryKey: { + columns: [{ columnName: 'id' }], + }, + foreignKeys: [{ refTableName: 't1', columns: [{ columnName: 'valfk', refColumnName: 'id' }] }], + }) + ); + runCommandOnDriver(conn, driver, dmp => dmp.put("insert into ~t1 (~id, ~val) ~values (1, 'first')")); + + const gett2 = () => + stream.Readable.from([ + { __isStreamHeader: true, __isDynamicStructure: true }, + { id: 1, val: 'v1', valfk: 1 }, + { id: 2, val: 'v2', valfk: 2 }, + ]); + + await dataDuplicator({ + systemConnection: conn, + driver, + items: [ + { + name: 't2', + operation: 'copy', + openStream: gett2, + }, + ], + options: { + setNullForUnresolvedNullableRefs: true, + }, + }); + + const res1 = await driver.query(conn, `select count(*) as cnt from t1`); + expect(res1.rows[0].cnt.toString()).toEqual('1'); + + const res2 = await driver.query(conn, `select count(*) as cnt from t2`); + expect(res2.rows[0].cnt.toString()).toEqual('1'); + }) + ); }); diff --git a/packages/datalib/src/DataDuplicator.ts b/packages/datalib/src/DataDuplicator.ts index 421ada4ee..c2b4d6cce 100644 --- a/packages/datalib/src/DataDuplicator.ts +++ b/packages/datalib/src/DataDuplicator.ts @@ -21,6 +21,7 @@ export interface DataDuplicatorItem { export interface DataDuplicatorOptions { rollbackAfterFinish?: boolean; skipRowsWithUnresolvedRefs?: boolean; + setNullForUnresolvedNullableRefs?: boolean; } class DuplicatorReference { @@ -36,9 +37,19 @@ class DuplicatorReference { } } +class DuplicatorWeakReference { + constructor(public base: DuplicatorItemHolder, public ref: TableInfo, public foreignKey: ForeignKeyInfo) {} + + get columnName() { + return this.foreignKey.columns[0].columnName; + } +} + class DuplicatorItemHolder { references: DuplicatorReference[] = []; backReferences: DuplicatorReference[] = []; + // not mandatory references to entities out of the model + weakReferences: DuplicatorWeakReference[] = []; table: TableInfo; isPlanned = false; idMap = {}; @@ -65,13 +76,19 @@ class DuplicatorItemHolder { for (const fk of this.table.foreignKeys) { if (fk.columns?.length != 1) continue; const refHolder = this.duplicator.itemHolders.find(y => y.name.toUpperCase() == fk.refTableName.toUpperCase()); - if (refHolder == null) continue; const isMandatory = this.table.columns.find(x => x.columnName == fk.columns[0]?.columnName)?.notNull; - const newref = new DuplicatorReference(this, refHolder, isMandatory, fk); - this.references.push(newref); - this.refByColumn[newref.columnName] = newref; + if (refHolder == null) { + if (!isMandatory) { + const weakref = new DuplicatorWeakReference(this, this.duplicator.db.tables.find(x => x.pureName == fk.refTableName), fk); + this.weakReferences.push(weakref); + } + } else { + const newref = new DuplicatorReference(this, refHolder, isMandatory, fk); + this.references.push(newref); + this.refByColumn[newref.columnName] = newref; - refHolder.isReferenced = true; + refHolder.isReferenced = true; + } } } diff --git a/packages/web/src/tabs/DataDuplicatorTab.svelte b/packages/web/src/tabs/DataDuplicatorTab.svelte index 562eaa5ae..12259b825 100644 --- a/packages/web/src/tabs/DataDuplicatorTab.svelte +++ b/packages/web/src/tabs/DataDuplicatorTab.svelte @@ -286,6 +286,29 @@ }} /> + + { + setEditorData(old => ({ + ...old, + setNullForUnresolvedNullableRefs: !$editorState.value?.setNullForUnresolvedNullableRefs, + })); + }, + }} + > + { + setEditorData(old => ({ + ...old, + setNullForUnresolvedNullableRefs: e.target.checked, + })); + }} + /> +