-
Notifications
You must be signed in to change notification settings - Fork 43
Add type-safe API for creating temporary views #172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| extension Table where Self: _Selection { | ||
| /// A `CREATE TEMPORARY VIEW` statement that executes after a database event. | ||
| /// | ||
| /// See <doc:Triggers> for more information. | ||
| /// | ||
| /// > Important: A name for the trigger is automatically derived from the arguments if one is not | ||
| /// > provided. If you build your own trigger helper that call this function, then your helper | ||
| /// > should also take `fileID`, `line` and `column` arguments and pass them to this function. | ||
| /// | ||
| /// - Parameters: | ||
| /// - name: The trigger's name. By default a unique name is generated depending using the table, | ||
| /// operation, and source location. | ||
| /// - ifNotExists: Adds an `IF NOT EXISTS` clause to the `CREATE TRIGGER` statement. | ||
| /// - operation: The trigger's operation. | ||
| /// - fileID: The source `#fileID` associated with the trigger. | ||
| /// - line: The source `#line` associated with the trigger. | ||
| /// - column: The source `#column` associated with the trigger. | ||
| /// - Returns: A temporary trigger. | ||
| public static func createTemporaryView<Selection: SelectStatement>( | ||
| ifNotExists: Bool = false, | ||
| select: () -> Selection | ||
| ) -> DatabaseView<Self, Selection> | ||
| where Selection.QueryValue == Columns.QueryValue { | ||
| DatabaseView(ifNotExists: ifNotExists, select: select()) | ||
| } | ||
| } | ||
|
|
||
| public struct DatabaseView<View: Table & _Selection, Selection: SelectStatement>: Statement | ||
| where Selection.QueryValue == View { | ||
| public typealias QueryValue = () | ||
| public typealias From = Never | ||
|
|
||
| fileprivate let ifNotExists: Bool | ||
| fileprivate let select: Selection | ||
|
|
||
| /// Returns a `DROP VIEW` statement for this trigger. | ||
| /// | ||
| /// - Parameter ifExists: Adds an `IF EXISTS` condition to the `DROP VIEW`. | ||
| /// - Returns: A `DROP VIEW` statement for this trigger. | ||
| public func drop(ifExists: Bool = false) -> some Statement<()> { | ||
| var query: QueryFragment = "DROP VIEW" | ||
| if ifExists { | ||
| query.append(" IF EXISTS") | ||
| } | ||
| query.append(" ") | ||
| if let schemaName = View.schemaName { | ||
| query.append("\(quote: schemaName).") | ||
| } | ||
| query.append(View.tableFragment) | ||
| return SQLQueryExpression(query) | ||
| } | ||
|
|
||
| public var query: QueryFragment { | ||
| var query: QueryFragment = "CREATE TEMPORARY VIEW" | ||
| if ifNotExists { | ||
| query.append(" IF NOT EXISTS") | ||
| } | ||
| query.append(.newlineOrSpace) | ||
| if let schemaName = View.schemaName { | ||
| query.append("\(quote: schemaName).") | ||
| } | ||
| query.append(View.tableFragment) | ||
| let columnNames: [QueryFragment] = View.TableColumns.allColumns | ||
| .map { "\(quote: $0.name)" } | ||
| query.append("\(.newlineOrSpace)(\(columnNames.joined(separator: ", ")))") | ||
| query.append("\(.newlineOrSpace)AS") | ||
| query.append("\(.newlineOrSpace)\(select)") | ||
| return query | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import Dependencies | ||
| import Foundation | ||
| import InlineSnapshotTesting | ||
| import StructuredQueries | ||
| import Testing | ||
| import _StructuredQueriesSQLite | ||
|
|
||
| extension SnapshotTests { | ||
| @Suite struct ViewsTests { | ||
| @Test func basics() { | ||
| let query = CompletedReminder.createTemporaryView { | ||
| Reminder | ||
| .where(\.isCompleted) | ||
| .select { CompletedReminder.Columns(reminderID: $0.id, title: $0.title) } | ||
| } | ||
stephencelis marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| assertQuery( | ||
| query | ||
| ) { | ||
| """ | ||
| CREATE TEMPORARY VIEW | ||
| "completedReminders" | ||
| ("reminderID", "title") | ||
| AS | ||
| SELECT "reminders"."id" AS "reminderID", "reminders"."title" AS "title" | ||
| FROM "reminders" | ||
| WHERE "reminders"."isCompleted" | ||
| """ | ||
| } results: { | ||
| """ | ||
|
|
||
| """ | ||
| } | ||
| assertQuery( | ||
| CompletedReminder.limit(2) | ||
| ) { | ||
| """ | ||
| SELECT "completedReminders"."reminderID", "completedReminders"."title" | ||
| FROM "completedReminders" | ||
| LIMIT 2 | ||
| """ | ||
| } results: { | ||
| """ | ||
| ┌────────────────────────┐ | ||
| │ CompletedReminder( │ | ||
| │ reminderID: 4, │ | ||
| │ title: "Take a walk" │ | ||
| │ ) │ | ||
| ├────────────────────────┤ | ||
| │ CompletedReminder( │ | ||
| │ reminderID: 7, │ | ||
| │ title: "Get laundry" │ | ||
| │ ) │ | ||
| └────────────────────────┘ | ||
| """ | ||
| } | ||
| assertQuery( | ||
| query.drop() | ||
| ) { | ||
| """ | ||
| DROP VIEW | ||
| "completedReminders" | ||
| """ | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Table @Selection | ||
| private struct CompletedReminder { | ||
| let reminderID: Reminder.ID | ||
| let title: String | ||
| } | ||
|
|
||
| extension Table where Self: _Selection { | ||
| static func foo() {} | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.