createContext method
GenerationContext
createContext(
- Insertable<
D> entry, - InsertMode mode, {
- UpsertClause<
T, D> ? onConflict, - bool returning = false,
Creates a GenerationContext which contains the sql necessary to run an
insert statement fro the entry
with the mode
.
This method is used internally by drift. Consider using insert instead.
Implementation
GenerationContext createContext(Insertable<D> entry, InsertMode mode,
{UpsertClause<T, D>? onConflict, bool returning = false}) {
_validateIntegrity(entry);
final rawValues = entry.toColumns(true);
// apply default values for columns that have one
final map = <String, Expression>{};
for (final column in table.$columns) {
final columnName = column.$name;
if (rawValues.containsKey(columnName)) {
final value = rawValues[columnName]!;
map[columnName] = value;
} else {
if (column.clientDefault != null) {
map[columnName] = column._evaluateClientDefault();
}
}
// column not set, and doesn't have a client default. So just don't
// include this column
}
final ctx = GenerationContext.fromDb(database);
if (ctx.dialect == SqlDialect.postgres &&
mode != InsertMode.insert &&
mode != InsertMode.insertOrIgnore) {
throw ArgumentError('$mode not supported on postgres');
}
ctx.buffer
..write(_insertKeywords[
ctx.dialect == SqlDialect.postgres ? InsertMode.insert : mode])
..write(' INTO ')
..write(table.$tableName)
..write(' ');
if (map.isEmpty) {
ctx.buffer.write('DEFAULT VALUES');
} else {
writeInsertable(ctx, map);
}
void writeDoUpdate(DoUpdate<T, D> onConflict) {
if (onConflict._usesExcludedTable) {
ctx.hasMultipleTables = true;
}
final upsertInsertable = onConflict._createInsertable(table);
if (!identical(entry, upsertInsertable)) {
// We run a ON CONFLICT DO UPDATE, so make sure upsertInsertable is
// valid for updates.
// the identical check is a performance optimization - for the most
// common call (insertOnConflictUpdate) we don't have to check twice.
table
.validateIntegrity(upsertInsertable, isInserting: false)
.throwIfInvalid(upsertInsertable);
}
final updateSet = upsertInsertable.toColumns(true);
ctx.buffer.write(' ON CONFLICT(');
final conflictTarget = onConflict.target ?? table.$primaryKey.toList();
if (conflictTarget.isEmpty) {
throw ArgumentError(
'Table has no primary key, so a conflict target is needed.');
}
var first = true;
for (final target in conflictTarget) {
if (!first) ctx.buffer.write(', ');
// Writing the escaped name directly because it should not have a table
// name in front of it.
ctx.buffer.write(target.escapedName);
first = false;
}
if (ctx.dialect == SqlDialect.postgres &&
mode == InsertMode.insertOrIgnore) {
ctx.buffer.write(') DO NOTHING ');
} else {
ctx.buffer.write(') DO UPDATE SET ');
first = true;
for (final update in updateSet.entries) {
final column = escapeIfNeeded(update.key);
if (!first) ctx.buffer.write(', ');
ctx.buffer.write('$column = ');
update.value.writeInto(ctx);
first = false;
}
if (onConflict._where != null) {
ctx.writeWhitespace();
final where = onConflict._where!(
table.asDslTable, table.createAlias('excluded').asDslTable);
where.writeInto(ctx);
}
}
}
if (onConflict is DoUpdate<T, D>) {
writeDoUpdate(onConflict);
} else if (onConflict is UpsertMultiple<T, D>) {
onConflict.clauses.forEach(writeDoUpdate);
}
if (returning) {
ctx.buffer.write(' RETURNING *');
} else if (ctx.dialect == SqlDialect.postgres) {
if (table.$primaryKey.length == 1) {
final id = table.$primaryKey.firstOrNull;
if (id != null && id.type is IntType) {
ctx.buffer.write(' RETURNING ${id.name}');
}
}
}
return ctx;
}