Skip to content

Conversation

@stephencelis
Copy link
Member

The result builder powering Table.insert currently expects the same type of value for each row inserted, which means they all need to be Table values, or they all need to be Table.Draft values, or they all need to be tuples of bindable values.

This PR adds a bit more flexibility to the builder so that different values can be inserted at the same time so long as they encode into the same set of values.

For example, table values can be inserted along drafts:

Reminder.insert {
  Reminder(id: UUID(), remindersListID: 1, title: "Milk")
  Reminder.Draft(id: UUID(), remindersListID: 1, title: "Milk")
}

And Swift values can be inserted alongside SQL:

Item.insert {
  $0.timestamp
} values: {
  Date()
  #sql("CURRENT_TIMESTAMP")
}

The PR also allows column expressions from applying @Table @Selection to be inserted:

Reminder.insert {
  Reminder.Columns(
    remindersListID: RemindersList.select { $0.id.min() },
    title: "New reminder"
  )
}

@stephencelis stephencelis requested a review from mbrandonw August 17, 2025 23:21
@attached(
extension,
conformances: QueryRepresentable,
conformances: Selection,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Selection now has its own conformance so that we can write generic algorithms against them. This is what allows them to be passed to Table.insert.

or conflictResolution: ConflictResolution? = nil,
_ columns: (TableColumns) -> TableColumns = { $0 },
@InsertValuesBuilder<Self> values: () -> [Self],
@InsertValuesBuilder<Self> values: () -> [[QueryFragment]],
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @InsertValuesBuilder is now responsible for building up an array of query fragments to insert.

@stephencelis stephencelis changed the base branch from main to fts August 17, 2025 23:23
///
/// - Parameter build: A result builder closure that prepares statements to insert every built row.
public init(@InsertValuesBuilder<any Table> _ build: () -> [any Table]) {
public init(@SeedsBuilder _ build: () -> [any Table]) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main gotcha in this PR is that we need a simultaneous release with SharingGRDB since it defines a seeds helper that will be incompatible with this feature.

)
}

private static func _insert<each ConflictTarget>(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many of these overloads can be removed as the logic was moved into the result builder.

Base automatically changed from fts to main August 20, 2025 22:23
@stephencelis stephencelis merged commit b5b5a9e into main Aug 20, 2025
3 checks passed
@stephencelis stephencelis deleted the better-insert-builder branch August 20, 2025 23:01
coenttb pushed a commit to coenttb/swift-structured-queries-postgres that referenced this pull request Oct 14, 2025
* Full-text search

* wip

* wip

* Update FTS5.swift

* wip

* wip

* wip

* Make inserts more flexible

* wip

* wip

* wip

* wip

* wip

* wip

* fix

* wip
coenttb pushed a commit to coenttb/swift-structured-queries-postgres that referenced this pull request Oct 15, 2025
* Full-text search

* wip

* wip

* Update FTS5.swift

* wip

* wip

* wip

* Make inserts more flexible

* wip

* wip

* wip

* wip

* wip

* wip

* fix

* wip
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants