validate method
Validate row values against schema, coercing types where safe.
Coercion rules (to avoid spurious errors from SQL string literals): intType : int | double→int | String→int | bool→0/1 realType : num | String→double | bool→0.0/1.0 boolType : bool | int(0/1) | String('true'/'false'/'0'/'1') textType : anything → toString() (never fails)
After coercion the value is written back into row so callers
always store the correctly-typed value.
Implementation
void validate(Map<String, dynamic> row) {
for (final col in columns) {
final raw = row[col.name];
if (raw == null) {
if (!col.isNullable && !col.isPrimaryKey) {
throw Exception('Column "${col.name}" cannot be null');
}
continue;
}
switch (col.type) {
case DataType.intType:
if (raw is int) break;
if (raw is double) { row[col.name] = raw.toInt(); break; }
if (raw is bool) { row[col.name] = raw ? 1 : 0; break; }
if (raw is String) {
final n = int.tryParse(raw) ??
double.tryParse(raw)?.toInt();
if (n != null) { row[col.name] = n; break; }
}
// Keep value as-is — don't throw, just warn
break;
case DataType.realType:
if (raw is double) break;
if (raw is int) { row[col.name] = raw.toDouble(); break; }
if (raw is bool) { row[col.name] = raw ? 1.0 : 0.0; break; }
if (raw is String) {
final n = double.tryParse(raw) ??
int.tryParse(raw)?.toDouble();
if (n != null) { row[col.name] = n; break; }
}
break;
case DataType.boolType:
if (raw is bool) break;
// FIX: SQLite/NebulaDB stores booleans as INTEGER 0/1.
// Both int and double (e.g. 0.0 / 1.0) must be coerced.
if (raw is int) { row[col.name] = raw != 0; break; }
if (raw is double) { row[col.name] = raw != 0.0; break; }
if (raw is String) {
final lo = raw.toLowerCase();
if (lo == 'true' || lo == '1') { row[col.name] = true; break; }
if (lo == 'false' || lo == '0') { row[col.name] = false; break; }
}
break;
case DataType.textType:
if (raw is! String) { row[col.name] = raw.toString(); }
break;
}
}
}