prepareImport method
Future<OrmImportPlan<T> >
prepareImport(
- Iterable<
T> entities, { - required List<
Column< conflictOn,T, dynamic> > - required List<
Column< update,T, dynamic> > - OrmImportDuplicates duplicates = OrmImportDuplicates.reject,
- OrmImportEmptyValues emptyValues = OrmImportEmptyValues.overwrite,
- String? validate(
- T entity
Callers parse/map CSV/JSON/etc to entities. Null or empty text preserves stored fields on conflict only when keepExisting is explicitly selected. NOCASE/RTRIM duplicate identity is resolved by SQLite, not Dart equality.
Implementation
Future<OrmImportPlan<T>> prepareImport(Iterable<T> entities,
{required List<Column<T, dynamic>> conflictOn,
required List<Column<T, dynamic>> update,
OrmImportDuplicates duplicates = OrmImportDuplicates.reject,
OrmImportEmptyValues emptyValues = OrmImportEmptyValues.overwrite,
String? Function(T entity)? validate}) {
try {
_writeType(type);
final keys = _projection(type, conflictOn),
fields = _projection(type, update);
if (keys.isEmpty ||
fields.isEmpty ||
fields.any((c) =>
c.keyOrder != null ||
c.generated ||
c.version ||
keys.contains(c))) {
throw const OrmMappingException(
'Import requires conflict keys and writable non-key/non-version update fields');
}
context._checkTrackingMutable();
final rows = <int, Map<String, Object?>>{};
final issues = <OrmImportIssue>[];
var count = 0;
for (final entity in entities) {
final index = count++;
if (count > 10000 || count * keys.length > 20000) {
throw RangeError('Import exceeds 10000 rows / 20000 key binds');
}
try {
final reason = validate?.call(entity);
if (reason != null) {
issues.add(OrmImportIssue(index, 'validation', reason));
continue;
}
final row = _copyOrmRow(type.encode(entity));
if (keys.any((c) => row[c.sqlName] == null)) {
issues.add(OrmImportIssue(
index, 'nullKey', 'Conflict keys cannot be NULL'));
} else {
rows[index] = row;
}
} catch (error) {
issues.add(OrmImportIssue(index, 'invalidValue',
'Validation/encoding failed: ${error.runtimeType}'));
}
}
return context._run(() async {
final collations = await context._uniqueTarget(type, keys);
if (rows.length > 1) {
final parameters = <Object?>[];
final values = [
for (final entry in rows.entries)
'(${entry.key},${keys.map((c) {
parameters.add(copyOrmValue(entry.value[c.sqlName]));
return '?';
}).join(',')})'
];
final names = [
for (var i = 0; i < keys.length; i++) quoteIdentifier('_k$i')
];
final groups = [
for (var i = 0; i < keys.length; i++)
'${names[i]} COLLATE ${quoteIdentifier(collations[i])}'
];
final repeated = await context.connection.query(
'WITH ${quoteIdentifier('_import')} (${quoteIdentifier('_i')},${names.join(',')}) AS (VALUES ${values.join(',')}) SELECT group_concat(${quoteIdentifier('_i')}) FROM ${quoteIdentifier('_import')} GROUP BY ${groups.join(',')} HAVING count(*)>1',
parameters);
for (final raw in repeated.rows) {
final indices = (raw.single as String)
.split(',')
.map(int.parse)
.toList()
..sort();
final kept = duplicates == OrmImportDuplicates.keepFirst
? indices.first
: indices.last;
for (final i in indices) {
if (duplicates != OrmImportDuplicates.reject && i == kept) {
continue;
}
rows.remove(i);
issues.add(OrmImportIssue(
i, 'duplicate', 'Repeated input conflict key',
error: duplicates == OrmImportDuplicates.reject));
}
}
}
issues.sort((a, b) => a.inputIndex.compareTo(b.inputIndex));
return OrmImportPlan._(this, count, rows.values.toList(), keys, fields,
emptyValues, collations, issues);
});
} catch (error, stack) {
return Future.error(error, stack);
}
}