Repository: MihaelIsaev/SwifQL Branch: master Commit: 465f7a5766ac Files: 202 Total size: 443.7 KB Directory structure: gitextract_0k67_6b0/ ├── .github/ │ └── workflows/ │ └── test.yml ├── .gitignore ├── LICENSE ├── Package.swift ├── README.md ├── Sources/ │ └── SwifQL/ │ ├── Alias.swift │ ├── Builders/ │ │ ├── CaseWhen.swift │ │ ├── Distinct.swift │ │ ├── GenericTableSelector.swift │ │ ├── NewColumn.swift │ │ ├── PostgresArray.swift │ │ ├── PostgresJsonObject.swift │ │ ├── QueryBuilder.swift │ │ ├── Schema/ │ │ │ ├── CreateSchemaBuilder.swift │ │ │ ├── DropSchemaBuilder.swift │ │ │ ├── UpdateSchemaChangeOwner.swift │ │ │ └── UpdateSchemaRename.swift │ │ ├── SwifQLJoinBuilder.swift │ │ ├── SwifQLSelectBuilder.swift │ │ ├── Table/ │ │ │ ├── CreateTableBuilder.swift │ │ │ ├── DropTableBuilder.swift │ │ │ └── UpdateTableBuilder.swift │ │ ├── Union.swift │ │ └── With.swift │ ├── Codable.swift │ ├── Column.swift │ ├── Constraint.swift │ ├── Dialect/ │ │ ├── Dialect+MySQL.swift │ │ ├── Dialect+Postgres.swift │ │ └── Dialect.swift │ ├── Enum.swift │ ├── Extensions/ │ │ ├── Array+SwifQLable.swift │ │ ├── Column+AutoType.swift │ │ ├── Decodable+Table.swift │ │ └── StringExtensions.swift │ ├── ExtractFieldValue.swift │ ├── FluentKitFieldable.swift │ ├── FormattedKeyPath.swift │ ├── Formatter.swift │ ├── Functions/ │ │ ├── Functions+Array.swift │ │ ├── Functions+General.swift │ │ ├── Functions+MySQL.swift │ │ ├── Functions+Numeric.swift │ │ ├── Functions+PostgresBool.swift │ │ ├── Functions+PostgresJSON.swift │ │ ├── Functions+PostgresJSONB.swift │ │ ├── Functions+PostgresSeries.swift │ │ ├── Functions+PostgresTime.swift │ │ ├── Functions+String.swift │ │ ├── Functions+TextSearch.swift │ │ ├── Functions+Window.swift │ │ └── Functions.swift │ ├── HybridOperator.swift │ ├── IndexItem.swift │ ├── IndexType.swift │ ├── KeyPath.swift │ ├── Keypathable.swift │ ├── Operators.swift │ ├── Parts/ │ │ ├── AliasPart.swift │ │ ├── ArrayPart.swift │ │ ├── BoolPart.swift │ │ ├── ColumnPart.swift │ │ ├── DatePart.swift │ │ ├── HybridOperatorPart.swift │ │ ├── KeyPathPart.swift │ │ ├── NullPart.swift │ │ ├── OperatorPart.swift │ │ ├── SafeValuePart.swift │ │ ├── TablePart.swift │ │ ├── TableWithAliasPart.swift │ │ └── UnsafeValuePart.swift │ ├── Path/ │ │ ├── Path+Column.swift │ │ ├── Path+Schema.swift │ │ ├── Path+SchemaWithTable.swift │ │ ├── Path+SchemaWithTableAndColumn.swift │ │ ├── Path+Table.swift │ │ ├── Path+TableWithColumn.swift │ │ └── Path.swift │ ├── Predicates.swift │ ├── Prepared.swift │ ├── QueryBuilderable.swift │ ├── QueryParts.swift │ ├── ReferentialAction.swift │ ├── ResultBuilders/ │ │ └── SwifQLableResultBuilder.swift │ ├── Schema.swift │ ├── Schemable.swift │ ├── SplittedQuery.swift │ ├── SwifQL.swift │ ├── SwifQLable+Parts/ │ │ ├── SwifQLable+Action.swift │ │ ├── SwifQLable+Add.swift │ │ ├── SwifQLable+AddQuery.swift │ │ ├── SwifQLable+After.swift │ │ ├── SwifQLable+All.swift │ │ ├── SwifQLable+Alter.swift │ │ ├── SwifQLable+And.swift │ │ ├── SwifQLable+Any.swift │ │ ├── SwifQLable+As.swift │ │ ├── SwifQLable+Asterisk.swift │ │ ├── SwifQLable+Before.swift │ │ ├── SwifQLable+Begin.swift │ │ ├── SwifQLable+Between.swift │ │ ├── SwifQLable+Cascade.swift │ │ ├── SwifQLable+Check.swift │ │ ├── SwifQLable+Column.swift │ │ ├── SwifQLable+Commit.swift │ │ ├── SwifQLable+Conflict.swift │ │ ├── SwifQLable+Constraint.swift │ │ ├── SwifQLable+Create.swift │ │ ├── SwifQLable+Default.swift │ │ ├── SwifQLable+Delete.swift │ │ ├── SwifQLable+Distinct.swift │ │ ├── SwifQLable+Do.swift │ │ ├── SwifQLable+Drop.swift │ │ ├── SwifQLable+End.swift │ │ ├── SwifQLable+Epoch.swift │ │ ├── SwifQLable+Exists.swift │ │ ├── SwifQLable+Filter.swift │ │ ├── SwifQLable+From.swift │ │ ├── SwifQLable+Fulltext.swift │ │ ├── SwifQLable+Function.swift │ │ ├── SwifQLable+GroupBy.swift │ │ ├── SwifQLable+Having.swift │ │ ├── SwifQLable+ILike.swift │ │ ├── SwifQLable+If.swift │ │ ├── SwifQLable+In.swift │ │ ├── SwifQLable+InsertInto.swift │ │ ├── SwifQLable+Interval.swift │ │ ├── SwifQLable+IsNotNull.swift │ │ ├── SwifQLable+IsNull.swift │ │ ├── SwifQLable+Items.swift │ │ ├── SwifQLable+Join.swift │ │ ├── SwifQLable+Key.swift │ │ ├── SwifQLable+Like.swift │ │ ├── SwifQLable+Limit.swift │ │ ├── SwifQLable+No.swift │ │ ├── SwifQLable+Not.swift │ │ ├── SwifQLable+NotBetween.swift │ │ ├── SwifQLable+NotExists.swift │ │ ├── SwifQLable+NotILike.swift │ │ ├── SwifQLable+NotIn.swift │ │ ├── SwifQLable+NotLike.swift │ │ ├── SwifQLable+Nothing.swift │ │ ├── SwifQLable+Null.swift │ │ ├── SwifQLable+Offset.swift │ │ ├── SwifQLable+On.swift │ │ ├── SwifQLable+Or.swift │ │ ├── SwifQLable+OrderBy.swift │ │ ├── SwifQLable+Over.swift │ │ ├── SwifQLable+Overlaps.swift │ │ ├── SwifQLable+Owner.swift │ │ ├── SwifQLable+PartitionBy.swift │ │ ├── SwifQLable+Primary.swift │ │ ├── SwifQLable+Raw.swift │ │ ├── SwifQLable+References.swift │ │ ├── SwifQLable+Rename.swift │ │ ├── SwifQLable+Restrict.swift │ │ ├── SwifQLable+Return.swift │ │ ├── SwifQLable+Returning.swift │ │ ├── SwifQLable+Rollback.swift │ │ ├── SwifQLable+Schema.swift │ │ ├── SwifQLable+Select.swift │ │ ├── SwifQLable+Semicolon.swift │ │ ├── SwifQLable+Set.swift │ │ ├── SwifQLable+Space.swift │ │ ├── SwifQLable+Subscript.swift │ │ ├── SwifQLable+Table.swift │ │ ├── SwifQLable+Timestamp.swift │ │ ├── SwifQLable+To.swift │ │ ├── SwifQLable+Type.swift │ │ ├── SwifQLable+Union.swift │ │ ├── SwifQLable+Unique.swift │ │ ├── SwifQLable+Update.swift │ │ ├── SwifQLable+Value.swift │ │ ├── SwifQLable+Values.swift │ │ ├── SwifQLable+Where.swift │ │ ├── SwifQLable+WhereExists.swift │ │ ├── SwifQLable+WhereNotExists.swift │ │ ├── SwifQLable+Window.swift │ │ └── SwifQLable+With.swift │ ├── SwifQLable.swift │ ├── SwifQLableArraySeparator.swift │ ├── Table.swift │ ├── TableAlias.swift │ ├── Type+Autodetect.swift │ ├── Type+SwifQLable.swift │ ├── Type.swift │ └── _Todo.swift └── Tests/ └── SwifQLTests/ ├── BuilderTests.swift ├── CaseTests.swift ├── DirectiveTests.swift ├── ExistsTests.swift ├── FnTests.swift ├── FromTests.swift ├── JsonTests.swift ├── OrderTests.swift ├── OtherTests.swift ├── PredicateTest.swift ├── SelectTests.swift ├── SubqueryTests.swift ├── SwifQLTestCase.swift ├── TableEncoding.swift └── WithTests.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/test.yml ================================================ name: test on: [push, pull_request] jobs: swift_6_0: container: swift:6.0 runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - run: swift test ================================================ FILE: .gitignore ================================================ Packages .build .DS_Store *.xcodeproj DerivedData/ Package.resolved .swiftpm ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 Mihael Isaev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Package.swift ================================================ // swift-tools-version:6.0 import PackageDescription let package = Package( name: "SwifQL", platforms: [ .macOS(.v10_15) ], products: [ // 💎 Swift lib that gives an ability to build complex raw SQL-queries in strong-type declarative way .library(name: "SwifQL", targets: ["SwifQL"]), ], dependencies: [], targets: [ .target(name: "SwifQL", dependencies: []), .testTarget(name: "SwifQLTests", dependencies: [.target(name: "SwifQL")]), ], swiftLanguageModes: [.v5] ) ================================================ FILE: README.md ================================================ [![Mihael Isaev](https://user-images.githubusercontent.com/1272610/53677263-7ecbfe00-3cc6-11e9-9049-2d2b9a2d7947.png)](http://mihaelisaev.com)

MIT License Swift 5.2 Github Actions Swift.Stream


This lib can be used either stand alone, or with frameworks like Vapor, Kitura, Perfect and others We recommend to use it with our [Bridges](https://github.com/SwifQL/Bridges) lib which is built on top of SwifQL and support all its flexibility It supports PostgreSQL and MySQL. And it's not so hard to add other dialects 🙂 just check [SwifQL/Dialect](https://github.com/SwifQL/SwifQL/tree/master/Sources/SwifQL/Dialect) folder Please feel free to ask any questions in issues, and also you could find me in the [Discord app](https://discordapp.com) as `@iMike#3049` or even better just join **#swifql** channel on [Vapor's Discord server](https://discord.gg/vapor) 🙂 > NOTE: > > If you haven't found some functions available out-of-the-box > then please check files like `SwifQLable+Select` and others in `Sources/SwifQL` folder > to ensure how easy it is to extend SwifQL to support anything you need 🚀 > > And feel free to send pull requests with your awesome new extensions ❤️ ### Support SwifQL development by giving a ⭐️ ## Installation ### With Vapor 4 + [Bridges](https://github.com/SwifQL/Bridges) + PostgreSQL ```swift .package(url: "https://github.com/vapor/vapor.git", from:"4.0.0-rc"), .package(url: "https://github.com/SwifQL/VaporBridges.git", from:"1.0.0-rc"), .package(url: "https://github.com/SwifQL/PostgresBridge.git", from:"1.0.0-rc"), .target(name: "App", dependencies: [ .product(name: "Vapor", package: "vapor"), .product(name: "VaporBridges", package: "VaporBridges"), .product(name: "PostgresBridge", package: "PostgresBridge") ]), ``` ### With Vapor 4 + [Bridges](https://github.com/SwifQL/Bridges) + MySQL ```swift .package(url: "https://github.com/vapor/vapor.git", from:"4.0.0-rc"), .package(url: "https://github.com/SwifQL/VaporBridges.git", from:"1.0.0-rc"), .package(url: "https://github.com/SwifQL/MySQLBridge.git", from:"1.0.0-rc"), .target(name: "App", dependencies: [ .product(name: "Vapor", package: "vapor"), .product(name: "VaporBridges", package: "VaporBridges"), .product(name: "MySQLBridge", package: "MySQLBridge") ]), ``` ### Pure ```swift .package(url: "https://github.com/MihaelIsaev/SwifQL.git", from:"2.0.0-beta"), .target(name: "App", dependencies: [ .product(name: "SwifQL", package: "SwifQL"), ]), ``` ### Pure on NIO2 ```swift .package(url: "https://github.com/MihaelIsaev/SwifQL.git", from:"2.0.0-beta"), .package(url: "https://github.com/MihaelIsaev/SwifQLNIO.git", from:"2.0.0"), .target(name: "App", dependencies: [ .product(name: "SwifQL", package: "SwifQL"), .product(name: "SwifQLNIO", package: "SwifQLNIO"), ]), ``` #### Pure on NIO1 (deprecated) ```swift .package(url: "https://github.com/MihaelIsaev/SwifQL.git", from:"1.0.0"), .package(url: "https://github.com/MihaelIsaev/SwifQLNIO.git", from:"1.0.0"), .target(name: "App", dependencies: ["SwifQL", "SwifQLNIO"]), ``` #### With Vapor 3 + Fluent (deprecated) ```swift .package(url: "https://github.com/MihaelIsaev/SwifQL.git", from:"1.0.0"), .package(url: "https://github.com/MihaelIsaev/SwifQLVapor.git", from:"1.0.0"), .target(name: "App", dependencies: ["Vapor", "SwifQL", "SwifQLVapor"]), ``` ## Philosophy This lib gives an ability to build absolutely any SQL query from simplest to monster complex. Example of simple query ```sql SELECT * FROM "User" WHERE "email" = 'john.smith@gmail.com' ``` build it with pure SwifQL this way ```swift SwifQL.select(User.table.*).from(User.table).where(\User.email == "john.smith@gmail.com") ``` or with SwifQL + [Bridges](https://github.com/SwifQL/Bridges) ```swift SwifQL.select(User.table.*).from(User.table).where(\User.$email == "john.smith@gmail.com") // or shorter User.select.where(\User.$email == "john.smith@gmail.com") ``` ## Usage ### Preparation > 💡 TIP: It is simpler and more powerful with [Bridges](https://github.com/SwifQL/Bridges) Of course you have to import the lib ```swift import SwifQL ``` #### For v1 Your table models should be conformed to `Tableable` protocol ```swift extension MyTable: Tableable {} ``` #### For v2 Your table models should be conformed to `Table` protocol ```swift extension MyTable: Table {} ``` ### How to build query > Instead of writing `Model.self` you should write `Model.table`, cause without Vapor you should conform your models to `Table`, and with Vapor its `Model`s are already conforms to `Table`. ```swift let query = SwifQL.select(\User.email, \User.name, \User.role) .from(User.table) .orderBy(.asc(\User.name)) .limit(10) ``` or with SwifQL + [Bridges](https://github.com/SwifQL/Bridges) ```swift let query = SwifQL.select(\User.$email, \User.$name, \User.$role) .from(User.table) .orderBy(.asc(\User.$name)) .limit(10) // or shorter User.select(\.$email, \.$name, \.$role).orderBy(.asc(\User.$name)).limit(10) ``` ### How to print raw query There are two options ##### 1. Get just plain query ```swift let rawSQLString = query.prepare(.psql).plain ``` or when using SwifQLSelectBuilder() - see below ```swift let rawSQLBuilderString = query.build().prepare(.psql).plain ``` ##### 2. Get object splitted into: formatted raw SQL string with $ symbols, and separated array with values ```swift let splittedQuery = query.prepare(.psql).splitted let formattedSQLQuery = splittedQuery.query // formatted raw SQL string with $ symbols instead of values let values = splittedQuery.values // an array of [Encodable] values ``` Then just put it into your database driver somehow 🙂 or use [Bridges](https://github.com/SwifQL/Bridges) ### How to execute query? SwifQL is only about building queries. For execution you have to use your favourite database driver. Below you can see an example for SwifQL + Vapor4 + [Bridges](https://github.com/SwifQL/Bridges) + PostgreSQL > 💡 You can get connection on both `Application` and `Request` objects. Example for `Application` object e.g. for `configure.swift` file ```swift // Called before your application initializes. public func configure(_ app: Application) throws { app.postgres.connection(to: .myDb1) { conn in SwifQL.select(User.table.*).from(User.table).execute(on: conn).all(decoding: User.self).flatMap { rows in print("yaaay it works and returned \(rows.count) rows!") } }.whenComplete { switch $0 { case .success: print("query was successful") case .failure(let error): print("query failed: \(error)") } } } ``` Example for `Request` object ```swift func routes(_ app: Application) throws { app.get("users") { req -> EventLoopFuture<[User]> in req.postgres.connection(to: .myDb1) { conn in SwifQL.select(User.table.*).from(User.table).execute(on: conn).all(decoding: User.self) } } } ``` > 💡 In examples above we use `.all(decoding: User.self)` for decoding results, but we also can use `.first(decoding: User.self).unwrap(or: Abort(.notFound))` to get only first row and unwrap it since it may be nil. ## Insert Into ### Single record SQL example ```sql INSERT INTO "User" ("email", "name") VALUES ('john@gmail.com', 'John Doe'), ('sam@gmail.com', 'Samuel Jackson') ``` SwifQL representation ```swift SwifQL.insertInto(User.table, fields: \User.email, \User.name).values("john@gmail.com", "John Doe") ``` or with SwifQL + [Bridges](https://github.com/SwifQL/Bridges) ```swift User(email: "john@gmail.com", name: "John Doe").insert(on: conn) ``` ### Batch SQL example ```sql INSERT INTO "User" ("email", "name") VALUES ('john@gmail.com', 'John Doe'), ('sam@gmail.com', 'Samuel Jackson') ``` SwifQL representation ```swift SwifQL.insertInto(User.table, fields: \User.email, \User.name).values(array: ["john@gmail.com", "John Doe"], ["sam@gmail.com", "Samuel Jackson"]) ``` or with SwifQL + [Bridges](https://github.com/SwifQL/Bridges) ```swift let user1 = User(email: "hello@gmail.com", name: "John") let user2 = User(email: "byebye@gmail.com", name: "Amily") let user3 = User(email: "trololo@gmail.com", name: "Trololo") [user1, user2, user3].batchInsert(on: conn) ``` ## Update ### General Update SQL example ```sql UPDATE "User" SET "name" = 'Mike' ``` SwifQL representation ```swift SwifQL.update(User.table).set[items: User.$name == "Mike"] ``` ### In Schema Update SQL example ```sql UPDATE "VIP"."User" SET "name" = 'Mike' ``` SwifQL representation ```swift let vip = User.inSchema("VIP") SwifQL.update(vip.table).set[items: vip.$name == "Mike"] ``` ## Builders For now there are only one implemented builder ### Select builder `SwifQLSelectBuilder` - by using it you could easily build a select query but in multiple lines without carying about ordering. ```swift let builder = SwifQLSelectBuilder() builder.where(\User.id == 1) builder.from(User.table) builder.limit(1) builder.select(User.table.*) let query = builder.build() return query.execute(on: req, as: .psql) .first(decoding: User.self) .unwrap(or: Abort(.notFound, reason: "User not found")) ``` So it will build query like: `SELECT "User".* FROM "User" WHERE "User"."id" = 1 LIMIT 1`. As you can see you shouldn't worry about parts ordering, it will sort them the right way before building. ### More builders Feel free to make your own builders and send pull request with it here! Also more conveniences are available in [Bridges](https://github.com/SwifQL/Bridges) lib which is created on top of SwifQL and support all its flexibility ## More query examples *Let's use `SwifQLSelectBuilder` for some next examples below, cause it's really convenient especially for complex queries.* 1. Let's imagine that you want to query count of users. ```swift /// Just query let query = SwifQL.select(Fn.count(\User.id) => "count").from(User.table) /// Execution and decoding for Vapor struct CountResult: Codable { let count: Int64 } query.execute(on: req, as: .psql) .first(decoding: CountResult.self) .unwrap(or: Abort(.notFound)) // returns Future ``` Here you can see two interesting things: `Fn.count()` and `=> "count"` `Fn` is a collection of function builders, so just call `Fn.` and take a look at the functions list on autocompletion. `=>` uses for two things: 1) to write alias through `as` 2) to cast values to some other types `// TBD: Expand list of examples` ## Aliasing Use `=>` operator for that, e.g.: If you want to write `SELECT "User"."email" as eml` then do it like this `SwifQL.select(\User.email => "eml")` Or if to speak about table name aliasing: If you want to reach `"User" as u` then do it like this `User.as("u")` And then keypaths will work like ```swift let u = User.as("u") let emailKeypath = u.email ``` ## Type casting Use `=>` operator for that, e.g.: If you want to write `SELECT "User"."email"::text` then do it like this `SwifQL.select(\User.email => .text)` ## Predicates | Infix operator | SQL equivalent | | ------- | -------------- | | > | > | | >= | >= | | < | < | | <= | <= | | == | = | | == nil | IS NULL | | != | != | | != nil | IS NOT NULL | | && | AND | And also `||` is for `OR` `||>` is for `@>` `<||` is for `<@` > Please feel free to add more predicates in `Predicates.swift` 😉 ## Operators Please feel free to take a look at `Fn.Operator` enum in `Functions.swift` ## Functions Please feel free to take a look at the list of function in `Functions.swift` ## Postgres JSON Object You could build JSON objects by using `PostgresJsonObject` SQL example ```sql jsonb_build_object('id', "User"."id", 'email', "User"."email") ``` SwifQL representation ```swift PgJsonObject().field(key: "id", value: \User.id).field(key: "email", value: \User.email) ``` ## Postgres Array You could build PostgreSQL arrays by using `PostgresArray` SQL example ```sql $$[]$$ ARRAY[] ARRAY[1,2,3] $$[]$$::uuid[] ARRAY[]::text[] ``` SwifQL representation ```swift PgArray(emptyMode: .dollar) PgArray() PgArray(1, 2, 3) PgArray(emptyMode: .dollar) => .uuidArray PgArray() => .textArray ``` Postgress range query examples ```swift // var ingredients: [IngredientsEnum] SwifQL.select(FoodMenu.table.*).WHERE( \FoodMenu.$ingredients ||> [.tomato] ) // var ingredients: [String] SwifQL.select(FoodMenu.table.*).WHERE( \FoodMenu.$ingredients ||> PgArray(["tomato"]) ) // var vendors: [UUID] SwifQL.select(FoodMenu.table.*).WHERE( \FoodMenu.$vendors ||> PgArray([vendorUuid]) ) ``` ## Nesting array of objects inside of query result Consider such response object you want to achieve: ```swift struct Book { let title: String let authors: [Author] } struct Author { let name: String } ``` you have to build it with use of subquery to dump Authors in JSON array and then attach them to result query. This will allow you to get all `Books` with their respective `Authors` This example uses Pivot table `BookAuthor` to join `Books` with their `Authors` ```swift let authors = SwifQL.select(Fn.coalesce(Fn.array_agg(Fn.to_jsonb(Author.table)), PgArray() => .jsonbArray)) let query = SwifQLSelectBuilder() query.select(Book.table.*) query.from(Book.table) query.join(.left, BookAuthor.table, on: \Book.$id == \BookAuthor.$bookID) query.join(.left, Author.table, on: \Author.$id == \BookAuthor.$authorID) // then query.group(...) as required in your case ``` ## FILTER SQL example ```sql COUNT("User"."id") FILTER (WHERE \User.isAdmin = TRUE) as "admins" ``` SwifQL representation ```swift Fn.count(\User.id).filter(where: \User.isAdmin == true) => "admins" ``` ## CASE ... WHEN ... THEN ... END SQL example ```sql CASE WHEN "User"."email" IS NULL THEN NULL ELSE "User"."email" END ``` SwifQL representation ```swift Case.when(\User.email == nil).then(nil).else(\User.email).end // or as many cases as needed Case.when(...).then(...).when(...).then(...).when(...).then(...).else(...).end ``` ## Brackets Yes, we really often use round brackets in our queries, e.g. in where clauses or in subqueries. SwifQL provides you with `|` prefix and postfix operators which is representates `(` and `)`. So it's easy to wrap some part of query into brackets, e.g.: SQL example ```sql "User.role" = 'admin' OR ("User.role" = 'user' AND "User"."age" >= 21) ``` SwifQL representation ```swift let where = \User.role == .admin || |\User.role == .user && \User.age >= 21| ``` ## Keypaths | SQL | SwiftQL | SwiftQL + Bridges | | ------- | -------------- | -------------- | | `"User"` | `User.table` | `the same` | | `"User" as u` | `User.as("u")` you could declare it as `let u = User.as("u")` | `the same` | | `"User".*` | `User.table.*` | `the same` | | `u.*` | `u.*` | `the same` | | `"User"."email"` | `\User.email` | `\User.$email` | | `u."email"` | `u.email` | `u.$email` | | `"User"."jsonObject"->"jsonField"` | `\User.jsonObject.jsonField` | `only through full path for now` | | `"User"."jsonObject"->"jsonField"` | `Path.Table("User").column("jsonObject", "jsonField")` | `the same` | ## Tests For now tests coverage is maybe around 70%. If you have timе and interest please feel free to send pull requests with more tests. You could find tests in `Tests` folder ### How it works under the hood `SwifQL` object needed just to start writing query, but it's just an empty object that conforms to `SwifQLable`. You can build your query with everything which conforms to `SwifQLable`, because `SwifQLable` is that very piece which will be used for concatenation to build a query. > If you take a look at the lib's files you may realize that the most of files are just extensions to `SwifQLable`. All available operators like `select`, `from`, `where`, and `orderBy` realized just as a function in `SwifQLable` extension and these functions always returns `SwifQLable` as a result. That's why you can write a query by calling `SwifQL.select().from().where().orderBy()` one by one. That's awesome cause it feels like writing a raw SQL, but it also gives you an ordering limitation, so if you write `SwifQL.select().where().from()` then you'll get wrong query as a result. But this limitation is resolved by using special builders, like `SwifQLSelectBuilder` (read about it later below). So let's take a look how lib builds a simple `SELECT "User".* FROM "User" WHERE "User"."email" = 'john.smith@gmail.com'` query First of all we should split query into the parts. Almost every word and punctuation here is a `SwifQLable` piece. - `SELECT` is `Fn.Operator.select` - ` ` is `Fn.Operator.space` - `"User"` is `User.table` - `.*` is `postfix operator .*` - ` ` is `Fn.Operator.space` - `FROM` is `Fn.Operator.from` - `"User"` is `User.table` - ` ` is `Fn.Operator.space` - `WHERE` is `Fn.Operator.where` - ` ` is `Fn.Operator.space` - `"User"."email"` is `\User.email` keypath - ` ` is `Fn.Operator.space` - `==` is `infix operator ==` - ` ` is `Fn.Operator.space` - `'john.smith@gmail.com'` is `SwifQLPartUnsafeValue` (it means that this value should be passed as $1 to the database) That's crazy, but awesome, right? 😄 But it's under the hood, so no worries! 😃 I just wanted to explain, that if you need something more than already provided then you'll be able to add needed operators/functions easily just by writing little extensions. > And also there is no overhead, it works pretty fast, but I'd love to hear if you know how to make it faster. This way gives you almost absolute flexibility in building queries. More than that as lib support `SQLDialect`'s it will build this query different way for PostgreSQL and MySQL, e.g.: - PostgreSQL: `SELECT "User".* FROM "User" WHERE "User"."email" = 'john.smith@gmail.com'` - MySQL: `SELECT User.* FROM User WHERE User.email = 'john.smith@gmail.com'` ## Contributing Please feel free to contribute! ================================================ FILE: Sources/SwifQL/Alias.swift ================================================ // // Alias.swift // SwifQL // // Created by Mihael Isaev on 19.04.2020. // import Foundation public protocol AnyAlias { var name: String { get } var inputValue: Encodable? { get } var isChanged: Bool { get } func encode(to encoder: Encoder) throws func decode(from decoder: Decoder) throws } public protocol AliasRepresentable { associatedtype Value: Codable var alias: Alias { get } } @propertyWrapper public final class Alias: AnyAlias, AliasRepresentable, ColumnRootNameable, Encodable where Value: Codable { public let name: String var outputValue: Value? public internal(set) var inputValue: Encodable? public var isChanged: Bool = false public var alias: Alias { self } public var columnName: String { alias.name } public var projectedValue: Alias { self } public var wrappedValue: Value { get { if let value = self.inputValue { return value as! Value } else if let value = self.outputValue { return value } else { fatalError("Cannot access field before it is initialized") } } set { self.inputValue = newValue self.isChanged = true } } public init(_ name: String) { self.name = name } /// See `Codable` public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(self.wrappedValue) } public func decode(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let valueType = Value.self as? _Optional.Type { if container.decodeNil() { self.wrappedValue = (valueType._none as! Value) } else { self.wrappedValue = try container.decode(Value.self) } } else { self.wrappedValue = try container.decode(Value.self) } self.isChanged = false } } protocol _Optional { static var _none: Any { get } } extension Optional: _Optional { static var _none: Any { return Self.none as Any } } // MARK: - KeyPath protocol _AliasKeyPath {} extension KeyPath: _AliasKeyPath where Value: AnyAlias {} extension KeyPath: SwifQLable, CustomStringConvertible where Root: ColumnRoot, Value: ColumnRootNameable { public var parts: [SwifQLPart] { if let kp = self as? Keypathable { return Path.Schema(kp.schema).table(kp.table).column(Root.key(for: self)).parts } return [SwifQLPartAlias(Root.key(for: self))] } } // MARK: - Aliasable public protocol Aliasable: ColumnRoot, Codable { init () } extension Aliasable { var columns: [(String, AnyAlias)] { return Mirror(reflecting: self) .children .compactMap { child in guard let property = child.value as? AnyAlias else { return nil } // remove underscore return (property.name, property) } } /// See `Codable` public init(from decoder: Decoder) throws { self.init() let container = try decoder.container(keyedBy: AliasCodingKey.self) try self.columns.forEach { label, property in let decoder = AliasContainerDecoder(container: container, key: .string(label)) try property.decode(from: decoder) } } public func encode(to encoder: Encoder) throws { let container = encoder.container(keyedBy: AliasCodingKey.self) try self.columns.forEach { label, property in let encoder = ContainerEncoder(container: container, key: .string(label)) try property.encode(to: encoder) } } } enum AliasCodingKey: CodingKey { case string(String) case int(Int) var stringValue: String { switch self { case .int(let int): return String(describing: int) case .string(let string): return string } } var intValue: Int? { switch self { case .int(let int): return int case .string(let string): return Int(string) } } init?(stringValue: String) { self = .string(stringValue) } init?(intValue: Int) { self = .int(intValue) } } private struct AliasContainerDecoder: Decoder, SingleValueDecodingContainer { let container: KeyedDecodingContainer let key: AliasCodingKey var codingPath: [CodingKey] { self.container.codingPath } var userInfo: [CodingUserInfoKey : Any] { [:] } func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { try self.container.nestedContainer(keyedBy: Key.self, forKey: self.key) } func unkeyedContainer() throws -> UnkeyedDecodingContainer { try self.container.nestedUnkeyedContainer(forKey: self.key) } func singleValueContainer() throws -> SingleValueDecodingContainer { self } func decode(_ type: T.Type) throws -> T where T : Decodable { try self.container.decode(T.self, forKey: self.key) } func decodeNil() -> Bool { do { return try self.container.decodeNil(forKey: self.key) } catch { return true } } } private struct ContainerEncoder: Encoder, SingleValueEncodingContainer { var container: KeyedEncodingContainer let key: AliasCodingKey var codingPath: [CodingKey] { self.container.codingPath } var userInfo: [CodingUserInfoKey : Any] { [:] } func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey { var container = self.container return container.nestedContainer(keyedBy: Key.self, forKey: self.key) } func unkeyedContainer() -> UnkeyedEncodingContainer { var container = self.container return container.nestedUnkeyedContainer(forKey: self.key) } func singleValueContainer() -> SingleValueEncodingContainer { self } mutating func encode(_ value: T) throws where T : Encodable { try self.container.encode(value, forKey: self.key) } mutating func encodeNil() throws { try self.container.encodeNil(forKey: self.key) } } ================================================ FILE: Sources/SwifQL/Builders/CaseWhen.swift ================================================ // // SwifQLable+Case.swift // SwifQL // // Created by Mihael Isaev on 15/02/2019. // import Foundation public class Case { var parts: [SwifQLPart] = [] public init (_ expression: SwifQLable? = nil) { parts.append(o: .case) if let expression = expression { parts.append(o: .space) parts.append(contentsOf: expression.parts) } } public static func when(_ expression: SwifQLable) -> Case { Case().when(expression) } public func when(_ expression: SwifQLable) -> Case { parts.appendSpaceIfNeeded() parts.append(o: .when) parts.append(o: .space) parts.append(contentsOf: expression.parts) return self } public func then(_ expression: SwifQLable?) -> Case { parts.appendSpaceIfNeeded() parts.append(o: .then) parts.append(o: .space) if let expression = expression { parts.append(contentsOf: expression.parts) } else { parts.append(o: .null) } return self } public func `else`(_ expression: SwifQLable?) -> Case { parts.appendSpaceIfNeeded() parts.append(o: .else) parts.append(o: .space) if let expression = expression { parts.append(contentsOf: expression.parts) } else { parts.append(o: .null) } return self } public var end: SwifQLable { parts.appendSpaceIfNeeded() parts.append(o: .end) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/Builders/Distinct.swift ================================================ // // Distinct.swift // SwifQL // // Created by Mihael Isaev on 02/03/2019. // import Foundation //MARK: DISTINCT public class Distinct: SwifQLable { public var parts: [SwifQLPart] public convenience init (_ field: SwifQLable...) { self.init(field) } public init (_ fields: [SwifQLable]) { parts = [] parts.append(o: .distinct) parts.append(o: .space) for (i, v) in fields.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } } public convenience init (on field: SwifQLable...) { self.init(on: field) } public init (on fields: [SwifQLable]) { parts = [] parts.append(o: .distinct) parts.append(o: .space) parts.append(o: .on) parts.append(o: .space) parts.append(o: .openBracket) for (i, v) in fields.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } parts.append(o: .closeBracket) } public func andAlso(_ fields: SwifQLable...) -> Distinct { andAlso(fields) } public func andAlso(_ fields: [SwifQLable]) -> Distinct { parts.append(o: .space) for (i, v) in fields.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return self } } ================================================ FILE: Sources/SwifQL/Builders/GenericTableSelector.swift ================================================ // // GenericTableSelector.swift // SwifQL // // Created by Mihael Isaev on 31.01.2020. // extension Table { public static var select: TableSelector { .init() } } public class TableSelector: SwifQLable { public var parts: [SwifQLPart] { build() } var columns: [String] = [] var exceptColumns: [String] = [] // MARK: Columns public func columns(_ a: KeyPath) -> Self where A: ColumnRepresentable { columns.append(T.key(for: a)) return self } public func columns( _ a: KeyPath, _ b: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath, _ i: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable, I: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) columns.append(T.key(for: i)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath, _ i: KeyPath, _ j: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable, I: ColumnRepresentable, J: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) columns.append(T.key(for: i)) columns.append(T.key(for: j)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath, _ i: KeyPath, _ j: KeyPath, _ k: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable, I: ColumnRepresentable, J: ColumnRepresentable, K: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) columns.append(T.key(for: i)) columns.append(T.key(for: j)) columns.append(T.key(for: k)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath, _ i: KeyPath, _ j: KeyPath, _ k: KeyPath, _ l: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable, I: ColumnRepresentable, J: ColumnRepresentable, K: ColumnRepresentable, L: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) columns.append(T.key(for: i)) columns.append(T.key(for: j)) columns.append(T.key(for: k)) columns.append(T.key(for: l)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath, _ i: KeyPath, _ j: KeyPath, _ k: KeyPath, _ l: KeyPath, _ m: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable, I: ColumnRepresentable, J: ColumnRepresentable, K: ColumnRepresentable, L: ColumnRepresentable, M: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) columns.append(T.key(for: i)) columns.append(T.key(for: j)) columns.append(T.key(for: k)) columns.append(T.key(for: l)) columns.append(T.key(for: m)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath, _ i: KeyPath, _ j: KeyPath, _ k: KeyPath, _ l: KeyPath, _ m: KeyPath, _ n: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable, I: ColumnRepresentable, J: ColumnRepresentable, K: ColumnRepresentable, L: ColumnRepresentable, M: ColumnRepresentable, N: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) columns.append(T.key(for: i)) columns.append(T.key(for: j)) columns.append(T.key(for: k)) columns.append(T.key(for: l)) columns.append(T.key(for: m)) columns.append(T.key(for: n)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath, _ i: KeyPath, _ j: KeyPath, _ k: KeyPath, _ l: KeyPath, _ m: KeyPath, _ n: KeyPath, _ o: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable, I: ColumnRepresentable, J: ColumnRepresentable, K: ColumnRepresentable, L: ColumnRepresentable, M: ColumnRepresentable, N: ColumnRepresentable, O: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) columns.append(T.key(for: i)) columns.append(T.key(for: j)) columns.append(T.key(for: k)) columns.append(T.key(for: l)) columns.append(T.key(for: m)) columns.append(T.key(for: n)) columns.append(T.key(for: o)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath, _ i: KeyPath, _ j: KeyPath, _ k: KeyPath, _ l: KeyPath, _ m: KeyPath, _ n: KeyPath, _ o: KeyPath, _ p: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable, I: ColumnRepresentable, J: ColumnRepresentable, K: ColumnRepresentable, L: ColumnRepresentable, M: ColumnRepresentable, N: ColumnRepresentable, O: ColumnRepresentable, P: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) columns.append(T.key(for: i)) columns.append(T.key(for: j)) columns.append(T.key(for: k)) columns.append(T.key(for: l)) columns.append(T.key(for: m)) columns.append(T.key(for: n)) columns.append(T.key(for: o)) columns.append(T.key(for: p)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath, _ i: KeyPath, _ j: KeyPath, _ k: KeyPath, _ l: KeyPath, _ m: KeyPath, _ n: KeyPath, _ o: KeyPath, _ p: KeyPath, _ q: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable, I: ColumnRepresentable, J: ColumnRepresentable, K: ColumnRepresentable, L: ColumnRepresentable, M: ColumnRepresentable, N: ColumnRepresentable, O: ColumnRepresentable, P: ColumnRepresentable, Q: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) columns.append(T.key(for: i)) columns.append(T.key(for: j)) columns.append(T.key(for: k)) columns.append(T.key(for: l)) columns.append(T.key(for: m)) columns.append(T.key(for: n)) columns.append(T.key(for: o)) columns.append(T.key(for: p)) columns.append(T.key(for: q)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath, _ i: KeyPath, _ j: KeyPath, _ k: KeyPath, _ l: KeyPath, _ m: KeyPath, _ n: KeyPath, _ o: KeyPath, _ p: KeyPath, _ q: KeyPath, _ r: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable, I: ColumnRepresentable, J: ColumnRepresentable, K: ColumnRepresentable, L: ColumnRepresentable, M: ColumnRepresentable, N: ColumnRepresentable, O: ColumnRepresentable, P: ColumnRepresentable, Q: ColumnRepresentable, R: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) columns.append(T.key(for: i)) columns.append(T.key(for: j)) columns.append(T.key(for: k)) columns.append(T.key(for: l)) columns.append(T.key(for: m)) columns.append(T.key(for: n)) columns.append(T.key(for: o)) columns.append(T.key(for: p)) columns.append(T.key(for: q)) columns.append(T.key(for: r)) return self } public func columns( _ a: KeyPath, _ b: KeyPath, _ c: KeyPath, _ d: KeyPath, _ e: KeyPath, _ f: KeyPath, _ g: KeyPath, _ h: KeyPath, _ i: KeyPath, _ j: KeyPath, _ k: KeyPath, _ l: KeyPath, _ m: KeyPath, _ n: KeyPath, _ o: KeyPath, _ p: KeyPath, _ q: KeyPath, _ r: KeyPath, _ s: KeyPath ) -> Self where A: ColumnRepresentable, B: ColumnRepresentable, C: ColumnRepresentable, D: ColumnRepresentable, E: ColumnRepresentable, F: ColumnRepresentable, G: ColumnRepresentable, H: ColumnRepresentable, I: ColumnRepresentable, J: ColumnRepresentable, K: ColumnRepresentable, L: ColumnRepresentable, M: ColumnRepresentable, N: ColumnRepresentable, O: ColumnRepresentable, P: ColumnRepresentable, Q: ColumnRepresentable, R: ColumnRepresentable, S: ColumnRepresentable { columns.append(T.key(for: a)) columns.append(T.key(for: b)) columns.append(T.key(for: c)) columns.append(T.key(for: d)) columns.append(T.key(for: e)) columns.append(T.key(for: f)) columns.append(T.key(for: g)) columns.append(T.key(for: h)) columns.append(T.key(for: i)) columns.append(T.key(for: j)) columns.append(T.key(for: k)) columns.append(T.key(for: l)) columns.append(T.key(for: m)) columns.append(T.key(for: n)) columns.append(T.key(for: o)) columns.append(T.key(for: p)) columns.append(T.key(for: q)) columns.append(T.key(for: r)) columns.append(T.key(for: s)) return self } // MARK: Except columns public func exceptColumn(_ column: KeyPath) -> Self where Column: ColumnRepresentable { exceptColumns.append(T.key(for: column)) return self } // MARK: Building private func build() -> [SwifQLPart] { var query = SwifQL if columns.count == 0 { if exceptColumns.count > 0 { var cols = T.init().columns.map { $0.name.label } if exceptColumns.count > 0 { cols = cols.filter { !exceptColumns.contains($0) } } query = query.select(cols.map { Path.Table(T.tableName).column($0) }) } else { query = query.select(T.table.*) } } else { var cols = columns if exceptColumns.count > 0 { cols = cols.filter { !exceptColumns.contains($0) } } query = query.select(cols.map { Path.Table(T.tableName).column($0) }) } query = query.from(T.table) return query.parts } } ================================================ FILE: Sources/SwifQL/Builders/NewColumn.swift ================================================ // // NewColumn.swift // // // Created by Mihael Isaev on 26.01.2020. // import Foundation public class NewColumn: SwifQLable { var name: String var type: Type var `default`: SwifQLable? var constraints: [SwifQLable] = [] public init(_ name: String, _ type: Type) { self.name = name self.type = type } @discardableResult public func `default`(constant v: Any) -> Self { `default` = SwifQLableParts(parts: SwifQLPartSafeValue(v)) return self } @discardableResult public func `default`(expression: SwifQLable) -> Self { `default` = expression return self } @discardableResult public func `default`(sequence name: String) -> Self { `default` = SwifQLableParts(parts: Op.custom(name)) return self } @discardableResult public func constraint(expression: SwifQLable) -> Self { constraints.append(expression) return self } @discardableResult public func primaryKey() -> Self { constraints.append(SwifQL.primary.key) return self } @discardableResult public func unique() -> Self { constraints.append(SwifQL.unique) return self } @discardableResult public func notNull() -> Self { constraints.append(SwifQL.not.null) return self } @discardableResult public func check(name: String? = nil, _ expression: SwifQLable) -> Self { guard expression.parts.count > 0 else { return self } var parts: [SwifQLPart] = [] if let name = name { parts.append(o: .constraint) parts.append(o: .space) parts.append(SwifQLPartColumn(name)) } parts.appendSpaceIfNeeded() parts.append(o: .check) parts.append(o: .openBracket) parts.append(contentsOf: expression.parts) parts.append(o: .closeBracket) constraints.append(SwifQLableParts(parts: parts)) return self } public var parts: [SwifQLPart] { var parts: [SwifQLPart] = [] parts.append(SwifQLPartColumn(name)) parts.append(o: .space) parts.append(o: .custom(type.name)) if let expression = `default` { parts.append(o: .space) parts.append(contentsOf: expression.parts) } constraints.forEach { expression in parts.append(o: .space) parts.append(contentsOf: expression.parts) } return parts } } ================================================ FILE: Sources/SwifQL/Builders/PostgresArray.swift ================================================ // // PostgresArray.swift // SwifQL // // Created by Mihael Isaev on 14/02/2019. // import Foundation public typealias PgArray = PostgresArray public struct PostgresArray: SwifQLable { public enum EmptyMode { case simple, dollar } public var parts: [SwifQLPart] = [] public init (_ items: SwifQLable..., emptyMode: EmptyMode = .simple) { self.init(items, emptyMode: emptyMode) } public init (_ items: [SwifQLable], emptyMode: EmptyMode = .simple) { if items.count == 0 && emptyMode == .dollar { parts.append(o: .doubleDollar) parts.append(o: .openSquareBracket) parts.append(o: .closeSquareBracket) parts.append(o: .doubleDollar) return } parts.append(o: .array) parts.append(o: .openSquareBracket) for (i, v) in items.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } parts.append(o: .closeSquareBracket) } } ================================================ FILE: Sources/SwifQL/Builders/PostgresJsonObject.swift ================================================ // // PostgresJsonObject.swift // SwifQL // // Created by Mihael Isaev on 14/02/2019. // import Foundation public typealias PgJsonObject = PostgresJsonObject public class PostgresJsonObject: SwifQLable { private struct Field { enum KeyMode { case `default`, keyPath } let key: SwifQLable let mode: KeyMode let value: SwifQLable } private var fields: [Field] = [] public var parts: [SwifQLPart] { var parts: [SwifQLPart] = [] parts.appendSpaceIfNeeded() var body: [SwifQLPart] = [] for (i, v) in fields.enumerated() { if i > 0 { body.append(o: .comma) body.append(o: .space) } switch v.mode { case .default: body.append(contentsOf: v.key.parts) case .keyPath: if let key = v.key as? SwifQLUniversalKeyPathSimple { body.append(o: .custom(key.lastPath.singleQuotted)) } else { body.append(o: .custom(String(describing: v.key).singleQuotted)) } } body.append(o: .comma) body.append(o: .space) body.append(contentsOf: v.value.parts) } return Fn.build(.jsonb_build_object, body: body).parts } public init () {} public func field(key: SwifQLable, value: SwifQLable) -> PostgresJsonObject { let field = Field(key: key, mode: .default, value: value) fields.append(field) return self } public func field(keyPathAsKey: SwifQLable, value: SwifQLable) -> PostgresJsonObject { let field = Field(key: keyPathAsKey, mode: .keyPath, value: value) fields.append(field) return self } } ================================================ FILE: Sources/SwifQL/Builders/QueryBuilder.swift ================================================ // // QueryBuilder.swift // SwifQLCore // // Created by Mihael Isaev on 19.12.2019. // import Foundation public protocol QueryBuilderItemable { var values: [SwifQLable] { get } } public struct QueryBuilderItem: SwifQLable { public let parts: [SwifQLPart] public let values: [SwifQLable] public init (_ values: [SwifQLable]? = nil) { var parts: [SwifQLPart] = [] if let values = values { values.forEach { parts.append(contentsOf: $0.parts) } } self.parts = parts self.values = values ?? [] } } @resultBuilder public struct QueryBuilder { public typealias Block = () -> SwifQLable /// Builds an empty view from an block containing no statements, `{ }`. public static func buildBlock() -> SwifQLable { QueryBuilderItem() } /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`) through unmodified. public static func buildBlock(_ attr: SwifQLable) -> SwifQLable { QueryBuilderItem([attr]) } /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`) through unmodified. public static func buildBlock(_ attrs: SwifQLable...) -> SwifQLable { QueryBuilderItem(attrs) } /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`) through unmodified. public static func buildBlock(_ attrs: [SwifQLable]) -> SwifQLable { QueryBuilderItem(attrs) } /// Provides support for "if" statements in multi-statement closures, producing an `Optional` view /// that is visible only when the `if` condition evaluates `true`. public static func buildIf(_ content: SwifQLable?) -> SwifQLable { guard let content = content else { return QueryBuilderItem() } return QueryBuilderItem([content]) } /// Provides support for "if" statements in multi-statement closures, producing /// ConditionalContent for the "then" branch. public static func buildEither(first: SwifQLable) -> SwifQLable { QueryBuilderItem([first]) } /// Provides support for "if-else" statements in multi-statement closures, producing /// ConditionalContent for the "else" branch. public static func buildEither(second: SwifQLable) -> SwifQLable { QueryBuilderItem([second]) } } ================================================ FILE: Sources/SwifQL/Builders/Schema/CreateSchemaBuilder.swift ================================================ // // CreateSchemaBuilder.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // public class CreateSchemaBuilder: SwifQLable { public var parts: [SwifQLPart] { var query = SwifQL.create.schema if shouldCheckIfNotExists { query = query.if.not.exists } query = query[any: Path.Schema(Schema.schemaName)] return query.parts } var shouldCheckIfNotExists = false public init () {} public func checkIfNotExists() -> Self { shouldCheckIfNotExists = true return self } } ================================================ FILE: Sources/SwifQL/Builders/Schema/DropSchemaBuilder.swift ================================================ // // DropSchemaBuilder.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // public class DropSchemaBuilder: SwifQLable { public var parts: [SwifQLPart] { var query = SwifQL.drop.schema if shouldCheckIfExists { query = query.if.exists } query = query[any: Path.Schema(Schema.schemaName)] return query.parts } var shouldCheckIfExists = false public init () {} public func checkIfExists() -> Self { shouldCheckIfExists = true return self } } ================================================ FILE: Sources/SwifQL/Builders/Schema/UpdateSchemaChangeOwner.swift ================================================ // // UpdateSchemaChangeOwner.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // public class UpdateSchemaChangeOwner: SwifQLable { public var parts: [SwifQLPart] { var query = SwifQL.alter.schema query = query[any: Path.Schema(Schema.schemaName)] query = query.owner.to query = query[any: Path.Schema(newOwner)] return query.parts } var newOwner = "" public init () {} public func newOwner(_ name: String) -> Self { newOwner = name return self } } ================================================ FILE: Sources/SwifQL/Builders/Schema/UpdateSchemaRename.swift ================================================ // // UpdateSchemaRename.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // public class UpdateSchemaRenameBuilder: SwifQLable { public var parts: [SwifQLPart] { var query = SwifQL.alter.schema query = query[any: Path.Schema(Schema.schemaName)] query = query.rename.to query = query[any: Path.Schema(newName)] return query.parts } var newName = "" public init () {} public func newName(_ name: String) -> Self { newName = name return self } } ================================================ FILE: Sources/SwifQL/Builders/SwifQLJoinBuilder.swift ================================================ // // SwifQLJoinBuilder.swift // App // // Created by Mihael Isaev on 22/02/2019. // import Foundation public struct JoinMode { let parts: [SwifQLPartOperator] public init (_ parts: SwifQLPartOperator...) { self.parts = parts } public init (_ parts: [SwifQLPartOperator]) { self.parts = parts } public static var none: JoinMode { .init(.join) } public static var left: JoinMode { .init(.left, .space, .join) } public static var leftLateral: JoinMode { .init(.left, .space, .join, .space, .lateral) } public static var leftOuter: JoinMode { .init(.left, .space, .outer, .space, .join) } public static var leftOuterLateral: JoinMode { .init(.left, .space, .outer, .space, .join, .space, .lateral) } public static var right: JoinMode { .init(.right, .space, .join) } public static var rightLateral: JoinMode { .init(.right, .space, .join, .space, .lateral) } public static var rightOuter: JoinMode { .init(.right, .space, .outer, .space, .join) } public static var rightOuterLateral: JoinMode { .init(.right, .space, .outer, .space, .join, .space, .lateral) } public static var inner: JoinMode { .init(.inner, .space, .join) } public static var outer: JoinMode { .init(.outer, .space, .join) } public static var cross: JoinMode { .init(.cross, .space, .join) } public static var crossLateral: JoinMode { .init(.cross, .space, .join, .space, .lateral) } } public struct SwifQLJoinBuilder: SwifQLable { let mode: JoinMode let table: SwifQLable let predicates: SwifQLable? public init (_ mode: JoinMode? = nil, _ table: SwifQLable, on predicates: SwifQLable? = nil) { self.mode = mode ?? .none self.table = table self.predicates = predicates } public var parts: [SwifQLPart] { var parts: [SwifQLPart] = [] parts.appendSpaceIfNeeded() parts.append(contentsOf: mode.parts) parts.append(o: .space) parts.append(contentsOf: table.parts) if let predicates = predicates { parts.append(o: .space) parts.append(o: .on) parts.append(o: .space) parts.append(contentsOf: predicates.parts) } return parts } } ================================================ FILE: Sources/SwifQL/Builders/SwifQLSelectBuilder.swift ================================================ // // SwifQLSelectBuilder.swift // App // // Created by Mihael Isaev on 22/02/2019. // import Foundation public class SwifQLSelectBuilder: QueryBuilderable { var select: [SwifQLable] = [] var froms: [SwifQLable] = [] public var queryParts = QueryParts() public init() {} public func copy() -> SwifQLSelectBuilder { let copy = SwifQLSelectBuilder() copy.select = select copy.froms = froms copy.queryParts = queryParts.copy() return copy } // MARK: Select @discardableResult public func select(_ item: SwifQLable...) -> SwifQLSelectBuilder { select(item) } @discardableResult public func select(_ items: [SwifQLable]) -> SwifQLSelectBuilder { select.append(contentsOf: items) return self } // MARK: From @discardableResult public func from(_ item: SwifQLable...) -> SwifQLSelectBuilder { from(item) } @discardableResult public func from(_ items: [SwifQLable]) -> SwifQLSelectBuilder { froms.append(contentsOf: items) return self } public func build() -> SwifQLable { var query = SwifQL.select(select) if froms.count > 0 { query = query.from(froms) } return queryParts.appended(to: query) } } ================================================ FILE: Sources/SwifQL/Builders/Table/CreateTableBuilder.swift ================================================ // // CreateTableBuilder.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // public class CreateTableBuilder: SwifQLable { public var parts: [SwifQLPart] { var query = SwifQL.create.table if shouldCheckIfNotExists { query = query.if.not.exists } let table = Path.Schema(schemaName).table(T.tableName) query = query[any: table].newColumns(columns) return query.parts } var columns: [NewColumn] = [] var shouldCheckIfNotExists = false var schemaName: String? public init (schema: Schemable.Type? = nil) { self.schemaName = schema?.schemaName ?? (T.self as? Schemable.Type)?.schemaName } public init (schema: String) { self.schemaName = schema } public func column(_ newColumn: NewColumn) -> Self { columns.append(newColumn) return self } // MARK: KeyPath public func column(_ keyPath: KeyPath, _ type: SwifQL.`Type`) -> Self where V: ColumnRepresentable { column(keyPath, type: type, default: nil, constraints: []) } public func column(_ keyPath: KeyPath, _ type: SwifQL.`Type`, _ constraints: Constraint...) -> Self where V: ColumnRepresentable { column(keyPath, type: type, default: nil, constraints: constraints) } public func column(_ keyPath: KeyPath, _ type: SwifQL.`Type`, _ `default`: ColumnDefault, _ constraints: Constraint...) -> Self where V: ColumnRepresentable { column(keyPath, type: type, default: `default`, constraints: constraints) } public func column(_ keyPath: KeyPath, type: SwifQL.`Type`, `default`: ColumnDefault?, constraints: [Constraint]) -> Self where V: ColumnRepresentable { column(T.key(for: keyPath), type: type, default: `default`, constraints: constraints) } // MARK: String public func column(_ name: String, _ type: SwifQL.`Type`) -> Self { column(name, type: type, default: nil, constraints: []) } public func column(_ name: String, _ type: SwifQL.`Type`, _ constraints: Constraint...) -> Self { column(name, type: type, default: nil, constraints: constraints) } public func column(_ name: String, _ type: SwifQL.`Type`, _ `default`: ColumnDefault, _ constraints: Constraint...) -> Self { column(name, type: type, default: `default`, constraints: constraints) } public func column(_ name: String, type: SwifQL.`Type`, `default`: ColumnDefault?, constraints: [Constraint]) -> Self { let newColumn = NewColumn(name, type) if let expression = `default`?.query { newColumn.default(expression: expression) } constraints.forEach { newColumn.constraint(expression: $0.query) } columns.append(newColumn) return self } public func checkIfNotExists() -> Self { shouldCheckIfNotExists = true return self } } ================================================ FILE: Sources/SwifQL/Builders/Table/DropTableBuilder.swift ================================================ // // DropTableBuilder.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // public class DropTableBuilder: SwifQLable { public var parts: [SwifQLPart] { var query = SwifQL.drop.table if shouldCheckIfExists { query = query.if.exists } query = query[any: Path.Table(T.tableName)] return query.parts } var shouldCheckIfExists = false public init () {} public func checkIfExists() -> Self { shouldCheckIfExists = true return self } } ================================================ FILE: Sources/SwifQL/Builders/Table/UpdateTableBuilder.swift ================================================ // // UpdateTableBuilder.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // public class UpdateTableBuilder: SwifQLable { public var parts: [SwifQLPart] { let table = Path.Schema(schemaName).table(T.tableName) var parts: [SwifQLPart] = [] if combinedAlterActions.count > 0 { var combinedParts = SwifQL.alter.table[any: table].parts combinedParts.append(o: .space) combinedAlterActions.enumerated().forEach { i, action in if i > 0 { combinedParts.append(o: .comma) combinedParts.append(o: .space) } combinedParts.append(contentsOf: action) } combinedParts.append(o: .semicolon) parts.append(contentsOf: combinedParts) } standAloneAlterActions.forEach { action in var standAloneParts = SwifQL.alter.table[any: table].parts standAloneParts.append(o: .space) standAloneParts.append(contentsOf: action) standAloneParts.append(o: .semicolon) parts.append(contentsOf: standAloneParts) } otherActions.forEach { action in var actionParts = action actionParts.append(o: .semicolon) parts.append(contentsOf: actionParts) } if let newName = renameTableTo { var renameParts = SwifQL.alter.table[any: table].parts renameParts.append(o: .space) renameParts.append(o: .rename) renameParts.append(o: .space) renameParts.append(o: .to) renameParts.append(o: .space) renameParts.append(SwifQLPartColumn(newName)) renameParts.append(o: .semicolon) parts.append(contentsOf: renameParts) } return parts } var combinedAlterActions: [[SwifQLPart]] = [] var standAloneAlterActions: [[SwifQLPart]] = [] var otherActions: [[SwifQLPart]] = [] var renameTableTo: String? var schemaName: String? public init (schema: Schemable.Type? = nil) { self.schemaName = schema?.schemaName ?? (T.self as? Schemable.Type)?.schemaName } public init (schema: String) { self.schemaName = schema } // MARK: - RENAME TABLE /// For changing the table name. public func renameTable(to: String) -> Self { renameTableTo = to return self } // MARK: - ADD COLUMN public func addColumn(_ newColumn: NewColumn) -> Self { var parts: [SwifQLPart] = [] parts.append(o: .add) parts.append(o: .space) parts.append(o: .column) parts.append(o: .space) parts.append(contentsOf: newColumn.parts) combinedAlterActions.append(parts) return self } // MARK: KeyPath /// Adds a new column to a table. public func addColumn(_ keyPath: KeyPath, _ type: SwifQL.`Type`, checkIfNotExists: Bool = false) -> Self where V: ColumnRepresentable { addColumn(keyPath, type: type, default: nil, checkIfNotExists: checkIfNotExists, constraints: []) } /// Adds a new column to a table. public func addColumn(_ keyPath: KeyPath, _ type: SwifQL.`Type`, checkIfNotExists: Bool = false, _ constraints: Constraint...) -> Self where V: ColumnRepresentable { addColumn(keyPath, type: type, default: nil, checkIfNotExists: checkIfNotExists, constraints: constraints) } /// Adds a new column to a table. public func addColumn(_ keyPath: KeyPath, _ type: SwifQL.`Type`, _ `default`: ColumnDefault, checkIfNotExists: Bool = false, _ constraints: Constraint...) -> Self where V: ColumnRepresentable { addColumn(keyPath, type: type, default: `default`, checkIfNotExists: checkIfNotExists, constraints: constraints) } /// Adds a new column to a table. public func addColumn(_ keyPath: KeyPath, type: SwifQL.`Type`, `default`: ColumnDefault?, checkIfNotExists: Bool = false, constraints: [Constraint]) -> Self where V: ColumnRepresentable { addColumn(T.key(for: keyPath), type: type, default: `default`, checkIfNotExists: checkIfNotExists, constraints: constraints) } // MARK: String /// Adds a new column to a table. public func addColumn(_ name: String, _ type: SwifQL.`Type`, checkIfNotExists: Bool = false) -> Self { addColumn(name, type: type, default: nil, checkIfNotExists: checkIfNotExists, constraints: []) } /// Adds a new column to a table. public func addColumn(_ name: String, _ type: SwifQL.`Type`, checkIfNotExists: Bool = false, _ constraints: Constraint...) -> Self { addColumn(name, type: type, default: nil, checkIfNotExists: checkIfNotExists, constraints: constraints) } /// Adds a new column to a table. public func addColumn(_ name: String, _ type: SwifQL.`Type`, _ `default`: ColumnDefault, checkIfNotExists: Bool = false, _ constraints: Constraint...) -> Self { addColumn(name, type: type, default: `default`, checkIfNotExists: checkIfNotExists, constraints: constraints) } /// Adds a new column to a table. public func addColumn(_ name: String, type: SwifQL.`Type`, `default`: ColumnDefault?, checkIfNotExists: Bool = false, constraints: [Constraint]) -> Self { var parts: [SwifQLPart] = [] parts.append(o: .add) parts.append(o: .space) parts.append(o: .column) if checkIfNotExists { parts.append(o: .space) parts.append(o: .if) parts.append(o: .space) parts.append(o: .not) parts.append(o: .space) parts.append(o: .exists) } parts.append(o: .space) parts.append(SwifQLPartColumn(name)) parts.append(o: .space) parts.append(o: .custom(type.name)) if let expression = `default` { parts.append(o: .space) parts.append(contentsOf: expression.query.parts) } constraints.forEach { expression in parts.append(o: .space) parts.append(contentsOf: expression.query.parts) } combinedAlterActions.append(parts) return self } // MARK: - DROP COLUMN /// For dropping a table column. /// The constraints and indexes imposed on the columns will also be dropped. /// /// You should use "string" table names instead of KeyPaths to keep consistency with previous migrations. /// /// Usage: /// /// ```swift /// .dropColumn(\User.$name) /// .dropColumn(\User.$surname, checkIfExists: true) // default `false` /// .dropColumn(\User.$createdAt, cascade: true) // default `false` /// ``` public func dropColumn(_ keyPath: KeyPath, checkIfExists: Bool = false, cascade: Bool = false) -> Self where V: ColumnRepresentable { dropColumn(T.key(for: keyPath), checkIfExists: checkIfExists, cascade: cascade) } /// For dropping a table column. /// The constraints and indexes imposed on the columns will also be dropped. /// /// Usage: /// /// ```swift /// .dropColumn("abc") /// .dropColumn("xyz", checkIfExists: true) // default `false` /// .dropColumn("qwe", cascade: true) // default `false` /// ``` public func dropColumn(_ name: String, checkIfExists: Bool = false, cascade: Bool = false) -> Self { var parts = SwifQL.parts parts.append(o: .drop) parts.append(o: .space) parts.append(o: .column) if checkIfExists { parts.append(o: .space) parts.append(o: .if) parts.append(o: .space) parts.append(o: .exists) } parts.append(o: .space) parts.append(SwifQLPartColumn(name)) if cascade { parts.append(o: .space) parts.append(o: .cascade) } combinedAlterActions.append(parts) return self } // MARK: - SET DEFAULT /// Use for adding/changing the default value for a column. public func setDefault(_ keyPath: KeyPath, constant v: Any) -> Self where V: ColumnRepresentable { setDefault(T.key(for: keyPath), constant: v) } /// Use for adding/changing the default value for a column. public func setDefault(_ keyPath: KeyPath, expression: SwifQLable) -> Self where V: ColumnRepresentable { setDefault(T.key(for: keyPath), expression: expression) } /// Use for adding/changing the default value for a column. public func setDefault(_ keyPath: KeyPath, sequence name: String) -> Self where V: ColumnRepresentable { setDefault(T.key(for: keyPath), sequence: name) } /// Use for adding/changing the default value for a column. public func setDefault(_ name: String, constant v: Any) -> Self { var parts = SwifQL.parts parts.append(o: .alter) parts.append(o: .space) parts.append(o: .column) parts.append(o: .space) parts.append(SwifQLPartColumn(name)) parts.append(o: .space) parts.append(o: .set) parts.append(o: .space) parts.append(o: .default) parts.append(o: .space) parts.append(SwifQLPartSafeValue(v)) combinedAlterActions.append(parts) return self } /// Use for adding/changing the default value for a column. public func setDefault(_ name: String, expression: SwifQLable) -> Self { var parts = SwifQL.parts parts.append(o: .alter) parts.append(o: .space) parts.append(o: .column) parts.append(o: .space) parts.append(SwifQLPartColumn(name)) parts.append(o: .space) parts.append(o: .set) parts.append(o: .space) parts.append(o: .default) parts.append(o: .space) parts.append(contentsOf: expression.parts) combinedAlterActions.append(parts) return self } /// Use for adding/changing the default value for a column. public func setDefault(_ name: String, sequence: String) -> Self { var parts = SwifQL.parts parts.append(o: .alter) parts.append(o: .space) parts.append(o: .column) parts.append(o: .space) parts.append(SwifQLPartColumn(name)) parts.append(o: .space) parts.append(o: .set) parts.append(o: .space) parts.append(o: .default) parts.append(o: .space) parts.append(Op.custom(sequence)) combinedAlterActions.append(parts) return self } // MARK: - DROP DEFAULT /// Use for removing the default value for a column. public func dropDefault(_ keyPath: KeyPath) -> Self where V: ColumnRepresentable { dropDefault(T.key(for: keyPath)) } /// Use for removing the default value for a column. public func dropDefault(_ name: String) -> Self { var parts = SwifQL.parts parts.append(o: .alter) parts.append(o: .space) parts.append(o: .column) parts.append(o: .space) parts.append(SwifQLPartColumn(name)) parts.append(o: .space) parts.append(o: .drop) parts.append(o: .space) parts.append(o: .default) combinedAlterActions.append(parts) return self } // MARK: - SET NOT NULL /// Use for removing NOT NULL mark for a column. public func setNotNull(_ keyPath: KeyPath) -> Self where V: ColumnRepresentable { setNotNull(T.key(for: keyPath)) } /// Use for removing NOT NULL mark for a column. public func setNotNull(_ name: String) -> Self { var parts = SwifQL.parts parts.append(o: .alter) parts.append(o: .space) parts.append(o: .column) parts.append(o: .space) parts.append(SwifQLPartColumn(name)) parts.append(o: .space) parts.append(o: .set) parts.append(o: .space) parts.append(o: .not) parts.append(o: .space) parts.append(o: .null) combinedAlterActions.append(parts) return self } // MARK: - DROP NOT NULL /// Use for removing NOT NULL mark for a column. public func dropNotNull(_ keyPath: KeyPath) -> Self where V: ColumnRepresentable { dropNotNull(T.key(for: keyPath)) } /// Use for removing NOT NULL mark for a column. public func dropNotNull(_ name: String) -> Self { var parts = SwifQL.parts parts.append(o: .alter) parts.append(o: .space) parts.append(o: .column) parts.append(o: .space) parts.append(SwifQLPartColumn(name)) parts.append(o: .space) parts.append(o: .drop) parts.append(o: .space) parts.append(o: .not) parts.append(o: .space) parts.append(o: .null) combinedAlterActions.append(parts) return self } // MARK: - RENAME COLUMN /// For changing the table name or a column name. public func renameColumn(_ keyPath: KeyPath, to: String) -> Self where V: ColumnRepresentable { renameColumn(T.key(for: keyPath), to: to) } /// For changing the table name or a column name. public func renameColumn(_ name: String, to: String) -> Self { var parts = SwifQL.parts parts.append(o: .rename) parts.append(o: .space) parts.append(o: .column) parts.append(o: .space) parts.append(SwifQLPartColumn(name)) parts.append(o: .space) parts.append(o: .to) parts.append(o: .space) parts.append(SwifQLPartColumn(to)) standAloneAlterActions.append(parts) return self } // MARK: - DROP CONSTRAINT /// Use for dropping a table constraint. public func dropConstraint(_ name: String) -> Self { var parts = SwifQL.parts parts.append(o: .drop) parts.append(o: .space) parts.append(o: .constraint) parts.append(o: .space) parts.append(SwifQLPartColumn(name)) otherActions.append(parts) return self } // MARK: - ADD UNIQUE /// Use to add UNIQUE mark to one or several columns. public func addUnique(to columns: String...) -> Self { guard columns.count > 0 else { return self } var parts = SwifQL.parts parts.append(o: .add) parts.append(o: .space) parts.append(o: .unique) parts.append(o: .space) parts.append(o: .openBracket) columns.enumerated().forEach { i, name in if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(SwifQLPartColumn(name)) } parts.append(o: .closeBracket) combinedAlterActions.append(parts) return self } // MARK: - ADD PRIMARY KEY /// Use to add PRIMARY KEY mark to one or several columns. public func addPrimaryKey(to columns: String...) -> Self { guard columns.count > 0 else { return self } var parts = SwifQL.parts parts.append(o: .add) parts.append(o: .space) parts.append(o: .primary) parts.append(o: .space) parts.append(o: .key) parts.append(o: .space) parts.append(o: .openBracket) columns.enumerated().forEach { i, name in if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(SwifQLPartColumn(name)) } parts.append(o: .closeBracket) combinedAlterActions.append(parts) return self } // MARK: - DROP INDEX /// Drops index by its name. public func dropIndex(schema: String? = nil, name: String) -> Self { var parts = SwifQL.parts parts.append(o: .drop) parts.append(o: .space) parts.append(o: .index) parts.append(o: .space) if let schema = schema { parts.append(SwifQLPartColumn(schema)) parts.append(o: .period) } parts.append(SwifQLPartColumn(name)) otherActions.append(parts) return self } // MARK: - CREATE INDEX /// Creates index for one or several columns. public func createIndex(unique: Bool = false, name: String? = nil, items: IndexItem..., type: IndexType? = nil, where condition: SwifQLable? = nil) -> Self { createIndex(unique: unique, name: name, items: items, type: type, where: condition) } /// Creates index for one or several columns. public func createIndex(unique: Bool = false, name: String? = nil, items: [IndexItem], type: IndexType? = nil, where condition: SwifQLable? = nil) -> Self { guard items.count > 0 else { return self } var parts = SwifQL.parts parts.append(o: .create) if unique { parts.append(o: .space) parts.append(o: .unique) } parts.append(o: .space) parts.append(o: .index) if let name = name { parts.append(o: .space) parts.append(SwifQLPartColumn(name)) } parts.append(o: .space) parts.append(o: .on) parts.append(o: .space) parts.append(contentsOf: Path.Schema(schemaName).table(T.tableName).parts) if let type = type { parts.append(o: .space) parts.append(o: .using) parts.append(o: .space) parts.append(contentsOf: type.parts) parts.append(o: .space) } parts.append(o: .openBracket) items.enumerated().forEach { i, item in if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: item.parts) } parts.append(o: .closeBracket) if let condition = condition { parts.append(o: .space) parts.append(o: .where) parts.append(o: .space) parts.append(contentsOf: condition.parts) } otherActions.append(parts) return self } // MARK: - ADD CHECK /// A check constraint helps in validating the records that are being inserted into a table. /// We can do this by combining the ALTER TABLE command with the ADD CHECK statement. public func addCheck(constraintName: String? = nil, _ expression: SwifQLable) -> Self { var parts = SwifQL.parts parts.append(o: .add) if let constraintName = constraintName { parts.append(o: .space) parts.append(o: .constraint) parts.append(o: .space) parts.append(SwifQLPartColumn(constraintName)) } parts.append(o: .space) parts.append(o: .check) parts.append(o: .space) parts.append(o: .openBracket) parts.append(contentsOf: expression.parts) parts.append(o: .closeBracket) combinedAlterActions.append(parts) return self } // MARK: - ADD FOREIGN KEY public func addForeignKey(column: String, constraintName: String? = nil, schema: String? = nil, table: String, columns: String..., onDelete: ReferentialAction? = nil, onUpdate: ReferentialAction? = nil) -> Self { addForeignKey(column: column, constraintName: constraintName, schema: schema, table: table, columns: columns, onDelete: onDelete, onUpdate: onUpdate) } public func addForeignKey(column: String, constraintName: String? = nil, schema: String? = nil, table: String, columns: [String], onDelete: ReferentialAction? = nil, onUpdate: ReferentialAction? = nil) -> Self { guard columns.count > 0 else { return self } var parts = SwifQL.parts parts.append(o: .add) if let constraintName = constraintName { parts.append(o: .space) parts.append(o: .constraint) parts.append(o: .space) parts.append(SwifQLPartColumn(constraintName)) } parts.append(o: .space) parts.append(o: .foreign) parts.append(o: .space) parts.append(o: .key) parts.append(o: .space) parts.append(o: .openBracket) parts.append(SwifQLPartColumn(column)) parts.append(o: .closeBracket) parts.append(o: .space) parts.append(o: .references) parts.append(o: .space) parts.append(SwifQLPartTable(schema: schema, table: table)) parts.append(o: .openBracket) columns.enumerated().forEach { i, name in if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(SwifQLPartColumn(name)) } parts.append(o: .closeBracket) if let action = onDelete { parts.append(o: .space) parts.append(o: .on) parts.append(o: .space) parts.append(o: .delete) parts.append(o: .space) parts.append(contentsOf: action.parts) } if let action = onUpdate { parts.append(o: .space) parts.append(o: .on) parts.append(o: .space) parts.append(o: .update) parts.append(o: .space) parts.append(contentsOf: action.parts) } combinedAlterActions.append(parts) return self } // TODO: https://www.postgresql.org/docs/current/sql-altertable.html // MARK: - SET STATISTICS /// For setting the statistics-gathering target for each column for ANALYZE operations. // MARK: - SET STORAGE /// For setting the mode of storage for a column. /// This will determine where the column is held, whether inline, or in a supplementary table. // MARK: - SET WITHOUT OIDS /// Use for removing the old column of the table. // MARK: - OWNER /// For changing the owner of a table, sequence, index or a view to a certain user. // MARK: - CLUSTER /// For marking a table to be used for carrying out future cluster operations. } ================================================ FILE: Sources/SwifQL/Builders/Union.swift ================================================ // // Union.swift // SwifQL // // Created by Taylor McIntyre on 2020-01-15. // import Foundation //MARK: UNION public class Union: SwifQLable { public var parts: [SwifQLPart] public convenience init (_ selection: SwifQLable...) { self.init(selection) } public init (_ selections: [SwifQLable]) { parts = [SwifQLPartOperator.openBracket] for (i, v) in selections.enumerated() { if i > 0 { parts.append(o: .space) parts.append(o: .union) parts.append(o: .space) parts.append(o: .openBracket) } parts.append(contentsOf: v.parts) parts.append(o: .closeBracket) } } } ================================================ FILE: Sources/SwifQL/Builders/With.swift ================================================ // // With.swift // SwifQL // // Created by Taylor McIntyre on 2020-01-16. // import Foundation //MARK: WITH public class With: SwifQLable { public var parts: [SwifQLPart] public init(_ table: SwifQLable, columns: [SwifQLable] = [], _ query: SwifQLable) { parts = table.parts if !columns.isEmpty { parts.append(o: .space) parts.append(o: .openBracket) for (i, v) in columns.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } parts.append(o: .closeBracket) } parts.append(o: .space) parts.append(o: .as) parts.append(o: .space) parts.append(o: .openBracket) parts.append(contentsOf: query.parts) parts.append(o: .closeBracket) } } ================================================ FILE: Sources/SwifQL/Codable.swift ================================================ // // Codable.swift // SwifQL // // Created by Mihael Isaev on 25.04.2020. // import Foundation public protocol SwifQLCodable: Codable, SwifQLable {} extension SwifQLCodable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } public protocol SwifQLEncodable: Encodable, SwifQLable {} extension SwifQLEncodable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension Array: SwifQLCodable where Element: SwifQLCodable {} ================================================ FILE: Sources/SwifQL/Column.swift ================================================ // // Column.swift // SwifQL // // Created by Mihael Isaev on 26.01.2020. // import Foundation public protocol AnyColumn { var name: String { get } var type: SwifQL.`Type` { get } var `default`: ColumnDefault? { get } var constraints: [Constraint] { get } var inputValue: Encodable? { get } var isChanged: Bool { get } func encode(to encoder: Encoder) throws func decode(from decoder: Decoder) throws } public protocol ColumnRepresentable { associatedtype Value: Codable var column: Column { get } } #if swift(>=5.4) private protocol AnyOptional { static var nilValue: Self { get } } extension Optional: AnyOptional { static var nilValue: Optional { .none } } #endif @propertyWrapper public final class Column: AnyColumn, ColumnRepresentable, ColumnRootNameable, Encodable where Value: Codable { public let name: String public let type: SwifQL.`Type` public let `default`: ColumnDefault? public let constraints: [Constraint] var outputValue: Value? public internal(set) var inputValue: Encodable? public var isChanged: Bool = false public var column: Column { self } public var columnName: String { column.name } public var projectedValue: Column { self } public var wrappedValue: Value { get { if let value = self.inputValue { return value as! Value } else if let value = self.outputValue { return value } else { #if swift(>=5.4) if let type = Value.self as? AnyOptional.Type { return type.nilValue as! Value } #endif fatalError("Cannot access \"\(columnName)\" field before it is initialized or fetched") } } set { self.inputValue = newValue self.isChanged = true } } /// Type will be selected automatically based on Swift type public init(_ name: String, default: ColumnDefault? = nil, constraints: Constraint...) { let autoType = Self.autoType(constraints) self.name = name self.type = autoType.type self.default = `default` var constraints = constraints if !autoType.isOptional, !constraints.contains(where: { $0.isNotNull || $0.isPrimaryKey }) { constraints.append(.notNull) } self.constraints = constraints } public init(name: String, type: SwifQL.`Type`, default: ColumnDefault? = nil, constraints: Constraint...) { self.name = name self.type = type self.default = `default` self.constraints = constraints } /// See `Codable` public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(self.wrappedValue) } public func decode(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let valueType = Value.self as? _Optional.Type { if container.decodeNil() { self.wrappedValue = (valueType._none as! Value) } else { self.wrappedValue = try container.decode(Value.self) } } else { self.wrappedValue = try container.decode(Value.self) } self.isChanged = false } } public struct ColumnDefault { let query: SwifQLable init (_ query: SwifQLable) { self.query = query } public static func `default`(_ v: Any) -> ColumnDefault { var parts: [SwifQLPart] = [] parts.append(o: .default) parts.append(o: .space) parts.append(safe: v) return .init(SwifQLableParts(parts: parts)) } public static func `default`(_ expression: SwifQLable) -> ColumnDefault { var parts: [SwifQLPart] = [] parts.append(o: .default) parts.append(o: .space) parts.append(contentsOf: expression.parts) return .init(SwifQLableParts(parts: parts)) } public static func `default`(sequence name: String) -> ColumnDefault { .init(SwifQLableParts(parts: Op.custom(name))) } } public protocol ColumnRoot { init () static func key(for column: KeyPath) -> String where C: ColumnRootNameable } extension ColumnRoot { public static func key(for column: KeyPath) -> String where Column: ColumnRootNameable { Self.init()[keyPath: column].columnName } } public protocol ColumnRootNameable { var columnName: String { get } } ================================================ FILE: Sources/SwifQL/Constraint.swift ================================================ // // Constraint.swift // SwifQL // // Created by Mihael Isaev on 19.04.2020. // import Foundation public struct Constraint { let query: SwifQLable init (_ query: SwifQLable) { self.query = query } var isPrimaryKey = false var isNotNull = false public static var primaryKey: Constraint { var constraint = Constraint(SwifQL.primary.key) constraint.isPrimaryKey = true return constraint } public static var unique: Constraint { .init(SwifQL.unique) } public static var notNull: Constraint { var constraint = Constraint(SwifQL.not.null) constraint.isNotNull = true return constraint } public static func check(name: String? = nil, _ expression: SwifQLable) -> Constraint { var query = SwifQL if let name = name { query = query.constraint[any: Path.Column(name)] } return .init(query.check.values(expression)) } public static func references(_ table: T.Type, onDelete: ReferentialAction? = nil, onUpdate: ReferentialAction? = nil) -> Constraint { var schemaName: String? if let schemable = table as? Schemable.Type { schemaName = schemable.schemaName } return references(schemaName, table.tableName, onDelete: onDelete, onUpdate: onUpdate) } public static func references(_ schema: String? = nil, _ table: String, onDelete: ReferentialAction? = nil, onUpdate: ReferentialAction? = nil) -> Constraint { var query = SwifQL.references[any: Path.SchemaWithTable(schema: schema, table: table)] if let action = onDelete { query = query.on.delete[any: action] } if let action = onUpdate { query = query.on.update[any: action] } return .init(query) } } ================================================ FILE: Sources/SwifQL/Dialect/Dialect+MySQL.swift ================================================ // // Dialect+MySQL.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation class MySQLDialect: SQLDialect { override var id: String? { "mysql" } override func keyPath(_ keyPath: SwifQLPartKeyPath) -> String { var result = "" if let schema = keyPath.schema { result.append(schemaName(schema)) } if let table = keyPath.table { if result.count > 0 { result.append(".") } result.append(tableName(table)) } if let lastPath = keyPath.paths.last { if result.count > 0 { result.append(".") } result.append(lastPath) } return result } override func date(_ value: Date) -> String { Fn.from_unixtime(value.timeIntervalSince1970).prepare(self).plain } override func bindKey(_ i: Int) -> String { "?" } override var arrayStart: String { "'" } override var arrayEnd: String { "'" } } ================================================ FILE: Sources/SwifQL/Dialect/Dialect+Postgres.swift ================================================ // // Dialect+Postgres.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation class PostgreSQLDialect: SQLDialect { override var id: String? { "psql" } override func schemaName(_ value: String) -> String { value.doubleQuotted } override func tableName(_ value: String) -> String { value.doubleQuotted } override func alias(_ value: String) -> String { value.doubleQuotted } override func column(_ value: String) -> String { value.doubleQuotted } override func jsonField(_ value: String) -> String { value.singleQuotted } override func keyPath(_ keyPath: SwifQLPartKeyPath) -> String { var result = "" if let schema = keyPath.schema { result.append(schemaName(schema)) } if let table = keyPath.table { if result.count > 0 { result.append(".") } result.append(tableName(table)) } for (i, v) in keyPath.paths.enumerated() { if i == 0 { if result.count > 0 { result.append(".") } result.append(column(v)) } else { if keyPath.asText, i == keyPath.paths.count - 1 { result.append("->>") } else { result.append("->") } result.append(jsonField(v)) } } return result } private lazy var _dateFormatter = PostgresDateFormatter() override func date(_ value: Date) -> String { let date = _dateFormatter.string(from: value) => .timestamptz let result = |date| return result.prepare(self).plain } // returns $1 $2 $3 binding keys for PostgreSQL override func bindKey(_ i: Int) -> String { "$\(i)" } override var arrayStart: String { Operator.array._value + Operator.openSquareBracket._value } override var emptyArrayStart: String { "'" + Operator.openBrace._value } override var arrayEnd: String { Operator.closeSquareBracket._value } override var emptyArrayEnd: String { Operator.closeBrace._value + "'" } } class PostgresDateFormatter: DateFormatter { override init() { super.init() calendar = Calendar(identifier: .iso8601) locale = Locale(identifier: "en_US_POSIX") timeZone = TimeZone.current dateFormat = "yyyy-MM-dd HH:mm:ssZZZZZ" } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } ================================================ FILE: Sources/SwifQL/Dialect/Dialect.swift ================================================ // // Dialect.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation open class SQLDialect { open var id: String? { nil } public static var mysql: SQLDialect { MySQLDialect() } public static var psql: SQLDialect { PostgreSQLDialect() } public static var all: [SQLDialect] { [.psql, .mysql] } /// Good choice only for super short and universal queries like `BEGIN;`, `ROLLBACK;`, `COMMIT;` public static var any: SQLDialect { .init() } init () {} open func boolValue(_ value: Bool) -> String { value ? "TRUE" : "FALSE" } open var arrayStart: String { "" } open var emptyArrayStart: String { arrayStart } open var arraySeparator: String { Operator.comma._value } open var arrayEnd: String { "" } open var emptyArrayEnd: String { arrayEnd } open func schemaName(_ value: String) -> String { value } open func tableName(_ value: String) -> String { value } open func alias(_ value: String) -> String { value } open func column(_ value: String) -> String { value } open func stringValue(_ value: String) -> String { value.singleQuotted } open func uuidValue(_ value: UUID) -> String { stringValue(value.uuidString) } open func jsonField(_ value: String) -> String { value } open func tableName(_ tableName: String, andAlias alias: String) -> String { self.tableName(tableName) + " AS " + self.alias(alias) } open func keyPath(_ keyPath: SwifQLPartKeyPath) -> String { "" } open func date(_ value: Date) -> String { "" } open var null: String { "NULL" } open func safeValue(_ value: Any?) -> String { guard let value = value else { return null } switch value { case let v as String: return stringValue(v) case let v as UUID: return uuidValue(v) case let v as Bool: return boolValue(v) case let v as UInt: return String(describing: v) case let v as UInt8: return String(describing: v) case let v as UInt16: return String(describing: v) case let v as UInt32: return String(describing: v) case let v as UInt64: return String(describing: v) case let v as Int: return String(describing: v) case let v as Int8: return String(describing: v) case let v as Int16: return String(describing: v) case let v as Int32: return String(describing: v) case let v as Int64: return String(describing: v) case let v as Float: return String(describing: v) case let v as Double: return String(describing: v) case let v as Decimal: return String(describing: v) default: return stringValue(String(describing: "")) // TODO: } } // MARK: - Binding (for formatter) open var bindSymbol: String { "§§§" } open func bindKey(_ i: Int) -> String { "?" } } extension SQLDialect: Equatable { public static func == (lhs: SQLDialect, rhs: SQLDialect) -> Bool { lhs.id == rhs.id } } ================================================ FILE: Sources/SwifQL/Enum.swift ================================================ // // Enum.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation public protocol AnySwifQLEnum: Codable, SwifQLable { static var name: String { get } var anyRawValue: Any { get } } protocol AnySwifQLEnumArray { var items: [AnySwifQLEnum] { get } } extension Array: AnySwifQLEnumArray where Element: AnySwifQLEnum { var items: [AnySwifQLEnum] { self } } public protocol SwifQLEnum: AnySwifQLEnum, RawRepresentable, CaseIterable { static var name: String { get } } extension SwifQLEnum { public static var name: String { String(describing: Self.self).lowercased() } public var anyRawValue: Any { rawValue } } /// See `SwifQLable` extension SwifQLEnum { public var parts: [SwifQLPart] { [SwifQLPartSafeValue(rawValue)] } } /// Allows to compare enum with enum column /// /// Usage: /// /// ```swift /// \User.$status == UserStatus.banned /// ``` public func == (lhs: KeyPath, rhs: B.Value.RawValue) -> SwifQLable where A: Table, B: ColumnRepresentable, B: ColumnRootNameable, B.Value: SwifQLEnum { SwifQLPredicate(operator: .equal, lhs: lhs, rhs: SwifQLableParts(parts: SwifQLPartSafeValue(rhs))) } ================================================ FILE: Sources/SwifQL/Extensions/Array+SwifQLable.swift ================================================ // // Array+SwifQLable.swift // // // Created by Mihael Isaev on 26.01.2020. // import Foundation extension Array: SwifQLable where Element: SwifQLable { public var parts: [SwifQLPart] { if let _ = Element.self as? AnySwifQLEnum.Type { let values = compactMap { ($0 as? AnySwifQLEnum)?.anyRawValue as? String }.joined(separator: ",") return [SwifQLPartSafeValue("{\(values)}")] } if let s = self as? SwifQLCodable { return [SwifQLPartUnsafeValue(s)] } else { return separator(.comma).parts } } } extension Array where Element: SwifQLable { public func separator(_ separator: SwifQLableArraySeparator) -> SwifQLable { var parts: [SwifQLPart] = [] for (i, v) in enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return SwifQLableParts(parts: parts) } } extension Array: SwifQLPart where Element: SwifQLable {} extension Array: SwifQLPartArray where Element: SwifQLable { public var elements: [SwifQLable] { self } } ================================================ FILE: Sources/SwifQL/Extensions/Column+AutoType.swift ================================================ // // Column+AutoType.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation extension Column { struct AutoType { let type: SwifQL.`Type` let isOptional: Bool init (_ type: SwifQL.`Type`, _ isOptional: Bool) { self.type = type self.isOptional = isOptional } } static func autoType(_ constraints: [Constraint]) -> AutoType { var isOptional = false switch Value.self { case is Optional.Type: isOptional = true; fallthrough case is String.Type: return .init(.text, isOptional) case is Optional<[String]>.Type: isOptional = true; fallthrough case is [String].Type: return .init(.textArray, isOptional) case is Optional.Type: isOptional = true; fallthrough case is UUID.Type: return .init(.uuid, isOptional) case is Optional<[UUID]>.Type: isOptional = true; fallthrough case is [UUID].Type: return .init(.uuidArray, isOptional) case is Optional.Type: isOptional = true; fallthrough case is Double.Type: return .init(.decimal, isOptional) case is Optional<[Double]>.Type: isOptional = true; fallthrough case is [Double].Type: return .init(.decimalArray, isOptional) case is Optional.Type: isOptional = true; fallthrough case is Float.Type: return .init(.float4, isOptional) case is Optional<[Float]>.Type: isOptional = true; fallthrough case is [Float].Type: return .init(.float4Array, isOptional) case is Optional.Type: isOptional = true; fallthrough case is UInt.Type: fallthrough case is Optional.Type: isOptional = true; fallthrough case is UInt8.Type: fallthrough case is Optional.Type: isOptional = true; fallthrough case is UInt16.Type: fallthrough case is Optional.Type: isOptional = true; fallthrough case is UInt32.Type: fallthrough case is Optional.Type: isOptional = true; fallthrough case is UInt64.Type: return .init(.int, isOptional) case is Optional<[UInt]>.Type: isOptional = true; fallthrough case is [UInt].Type: fallthrough case is Optional<[UInt8]>.Type: isOptional = true; fallthrough case is [UInt8].Type: fallthrough case is Optional<[UInt16]>.Type: isOptional = true; fallthrough case is [UInt16].Type: fallthrough case is Optional<[UInt32]>.Type: isOptional = true; fallthrough case is [UInt32].Type: fallthrough case is Optional<[UInt64]>.Type: isOptional = true; fallthrough case is [UInt64].Type: return .init(.intArray, isOptional) case is Optional.Type: isOptional = true; fallthrough case is Int8.Type: fallthrough case is Optional.Type: isOptional = true; fallthrough case is Int16.Type: fallthrough case is Optional.Type: isOptional = true; fallthrough case is Int32.Type: fallthrough case is Optional.Type: isOptional = true; fallthrough case is Int.Type: if constraints.contains(where: { $0.isPrimaryKey }) { return .init(.serial, isOptional) } else { return .init(.int, isOptional) } case is Optional.Type: isOptional = true; fallthrough case is Int64.Type: if constraints.contains(where: { $0.isPrimaryKey }) { return .init(.bigserial, isOptional) } else { return .init(.bigint, isOptional) } case is Optional<[Int8]>.Type: isOptional = true; fallthrough case is [Int8].Type: fallthrough case is Optional<[Int16]>.Type: isOptional = true; fallthrough case is [Int16].Type: fallthrough case is Optional<[Int32]>.Type: isOptional = true; fallthrough case is [Int32].Type: fallthrough case is Optional<[Int]>.Type: isOptional = true; fallthrough case is [Int].Type: return .init(.intArray, isOptional) case is Optional<[Int64]>.Type: isOptional = true; fallthrough case is [Int64].Type: return .init(.bigintArray, isOptional) case is Optional.Type: isOptional = true; fallthrough case is Date.Type: return .init(.timestamptz, isOptional) case is Optional<[Date]>.Type: isOptional = true; fallthrough case is [Date].Type: return .init(.timestamptzArray, isOptional) case is Optional.Type: isOptional = true; fallthrough case is Data.Type: return .init(.bytea, isOptional) case is Optional<[Data]>.Type: isOptional = true; fallthrough case is [Data].Type: return .init(.byteaArray, isOptional) case is AnyOptionalEnum.Type: guard let t = Value.self as? AnyOptionalEnum.Type else { fallthrough } return .init(.custom(t.name), true) case is AnySwifQLEnum.Type: guard let t = Value.self as? AnySwifQLEnum.Type else { fallthrough } return .init(.custom(t.name), false) case is Optional<[Encodable]>.Type: isOptional = true; fallthrough case is [Encodable].Type: return .init(.jsonbArray, isOptional) case is ClosedRange.Type: return .init(.daterange, isOptional) case is Range.Type: return .init(.daterange, isOptional) default: return .init(.text, true) } } } fileprivate protocol AnyOptionalEnum { static var name: String { get } } extension Optional: AnyOptionalEnum where Wrapped: AnySwifQLEnum { fileprivate static var name: String { return Wrapped.name } } extension Optional { fileprivate static var trololo: Wrapped.Type { return Wrapped.self } } ================================================ FILE: Sources/SwifQL/Extensions/Decodable+Table.swift ================================================ // // Decodable+Table.swift // // // Created by Mihael Isaev on 26.01.2020. // import Foundation extension Decodable { public static var table: SwifQLable { let tableName: String if let model = Self.self as? AnyTable.Type { tableName = model.tableName } else { tableName = String(describing: Self.self) } if let schema = Self.self as? Schemable.Type { return Path.SchemaWithTable(schema: schema.schemaName, table: tableName) } else { return Path.Table(tableName) } } } ================================================ FILE: Sources/SwifQL/Extensions/StringExtensions.swift ================================================ // // StringExtensions.swift // App // // Created by Mihael Isaev on 06.06.2018. // import Foundation extension String { public var singleQuotted: String { "'\(self)'" } public var doubleQuotted: String { "\"\(self)\"" } public var roundBracketted: String { "(\(self))" } public static func singleQuotted(_ v: Any) -> String { "\(v)".singleQuotted } public static func doubleQuotted(_ v: Any) -> String { "\(v)".doubleQuotted } public static func roundBracketted(_ v: Any) -> String { "\(v)".roundBracketted } public func `as`(_ v: String) -> String { self + " as " + v } } ================================================ FILE: Sources/SwifQL/ExtractFieldValue.swift ================================================ // // ExtractFieldValue.swift // SwifQL // // Created by Mihael Isaev on 29/10/2019. // public struct ExtractFieldValue { public let value: String /// Postgres specific /// for TIMESTAMP: The century /// for Interval: The number of centuries public static var century: ExtractFieldValue { .init(value: "CENTURY") } /// Universal /// for TIMESTAMP: The day of the month (1-31) /// for Interval: The number of days public static var day: ExtractFieldValue { .init(value: "DAY") } /// Postgres specific /// for TIMESTAMP: The decade that is the year divided by 10 /// for Interval: Sames as TIMESTAMP public static var decade: ExtractFieldValue { .init(value: "DECADE") } /// Postgres specific /// for TIMESTAMP: The day of week Sunday (0) to Saturday (6) /// for Interval: N/A public static var dow: ExtractFieldValue { .init(value: "DOW") } /// Postgres specific /// for TIMESTAMP: The day of year that ranges from 1 to 366 /// for Interval: N/A public static var doy: ExtractFieldValue { .init(value: "DOY") } /// Postgres specific /// for TIMESTAMP: The number of seconds since 1970-01-01 00:00:00 UTC /// for Interval: The total number of seconds in the interval public static var epoch: ExtractFieldValue { .init(value: "EPOCH") } /// Universal /// for TIMESTAMP: The hour (0-23) /// for Interval: The number of hours public static var hour: ExtractFieldValue { .init(value: "HOUR") } /// Postgres specific /// for TIMESTAMP: Day of week based on ISO 8601 Monday (1) to Saturday (7) /// for Interval: N/A public static var isoDow: ExtractFieldValue { .init(value: "ISODOW") } /// Postgres specific /// for TIMESTAMP: ISO 8601 week number of year /// for Interval: N/A public static var isoYear: ExtractFieldValue { .init(value: "ISOYEAR") } /// Postgres specific /// for TIMESTAMP: The seconds field, including fractional parts, multiplied by 1000000 /// for Interval: Sames as TIMESTAMP public static var microseconds: ExtractFieldValue { .init(value: "MICROSECONDS") } /// Postgres specific /// for TIMESTAMP: The millennium /// for Interval: The number of millennium public static var millenium: ExtractFieldValue { .init(value: "MILLENNIUM") } /// Postgres specific /// for TIMESTAMP: The seconds field, including fractional parts, multiplied by 1000 /// for Interval: Sames as TIMESTAMP public static var milliseconds: ExtractFieldValue { .init(value: "MILLISECONDS") } /// Universal /// for TIMESTAMP: The minute (0-59) /// for Interval: The number of minutes public static var minute: ExtractFieldValue { .init(value: "MINUTE") } /// Universal /// for TIMESTAMP: Month, 1-12 /// for Interval: The number of months, modulo (0-11) public static var month: ExtractFieldValue { .init(value: "MONTH") } /// Universal /// for TIMESTAMP: Quarter of the year /// for Interval: The number of quarters public static var quarter: ExtractFieldValue { .init(value: "QUARTER") } /// Universal /// for TIMESTAMP: The second /// for Interval: The number of seconds public static var second: ExtractFieldValue { .init(value: "SECOND") } /// Postgres specific /// for TIMESTAMP: The timezone offset from UTC, measured in seconds /// for Interval: N/A public static var timeZone: ExtractFieldValue { .init(value: "TIMEZONE") } /// Postgres specific /// for TIMESTAMP: The hour component of the time zone offset /// for Interval: N/A public static var timeZoneHour: ExtractFieldValue { .init(value: "TIMEZONE_HOUR") } /// Postgres specific /// for TIMESTAMP: The minute component of the time zone offset /// for Interval: N/A public static var timeZoneMinute: ExtractFieldValue { .init(value: "TIMEZONE_MINUTE") } /// Universal /// for TIMESTAMP: The number of the ISO 8601 week-numbering week of the year /// for Interval: N/A public static var week: ExtractFieldValue { .init(value: "WEEK") } /// Universal /// for TIMESTAMP: The year /// for Interval: Sames as TIMESTAMP public static var year: ExtractFieldValue { .init(value: "YEAR") } /// MySQL specific public static var microsecond: ExtractFieldValue { .init(value: "MICROSECOND") } /// MySQL specific public static var secondMicrosecond: ExtractFieldValue { .init(value: "SECOND_MICROSECOND") } /// MySQL specific public static var minuteMicrosecond: ExtractFieldValue { .init(value: "MINUTE_MICROSECOND") } /// MySQL specific public static var minuteSecond: ExtractFieldValue { .init(value: "MINUTE_SECOND") } /// MySQL specific public static var hourMicrosecond: ExtractFieldValue { .init(value: "HOUR_MICROSECOND") } /// MySQL specific public static var hourSecond: ExtractFieldValue { .init(value: "HOUR_SECOND") } /// MySQL specific public static var hourMinute: ExtractFieldValue { .init(value: "HOUR_MINUTE") } /// MySQL specific public static var dayMicrosecond: ExtractFieldValue { .init(value: "DAY_MICROSECOND") } /// MySQL specific public static var daySecond: ExtractFieldValue { .init(value: "DAY_SECOND") } /// MySQL specific public static var dayMinute: ExtractFieldValue { .init(value: "DAY_MINUTE") } /// MySQL specific public static var dayHour: ExtractFieldValue { .init(value: "DAY_HOUR") } /// MySQL specific public static var yearMonth: ExtractFieldValue { .init(value: "YEAR_MONTH") } } ================================================ FILE: Sources/SwifQL/FluentKitFieldable.swift ================================================ public protocol FluentKitFieldable { var schema: String { get } var key: String { get } } ================================================ FILE: Sources/SwifQL/FormattedKeyPath.swift ================================================ // // FormattedKeyPath.swift // SwifQL // // Created by Mihael Isaev on 20/06/2019. // import Foundation /// Formatting keypath public typealias FKP = FormattedKeyPath public struct FormattedKeyPath { let _table: String let _paths: [String] public init (_ table: T.Type, _ paths: String...) { _table = table.tableName _paths = paths } public init (_ table: T.Type, _ paths: [String]) { _table = table.tableName _paths = paths } public init (_ table: String, _ paths: String...) { _table = table _paths = paths } public init (_ table: String, _ paths: [String]) { _table = table _paths = paths } } extension FormattedKeyPath: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartKeyPath(table: _table, paths: _paths)] } } extension FormattedKeyPath: KeyPathLastPath { public var lastPath: String { _paths.last ?? "" } } extension Table { /// Manual key path. Alias to `\User.something` public static func manualKeyPath(_ paths: String...) -> FormattedKeyPath { manualKeyPath(paths) } /// Manual key path. Alias to `\User.something` public static func manualKeyPath(_ paths: [String]) -> FormattedKeyPath { FormattedKeyPath(tableName, paths) } /// Manual key path. Alias to `\User.something` public static func mkp(_ paths: String...) -> FormattedKeyPath { manualKeyPath(paths) } /// Manual key path. Alias to `\User.something` public static func mkp(_ paths: [String]) -> FormattedKeyPath { manualKeyPath(paths) } } ================================================ FILE: Sources/SwifQL/Formatter.swift ================================================ // // Formatter.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation struct SwifQLFormatter { private let dialect: SQLDialect private let mode: Mode init (_ dialect: SQLDialect, mode: Mode) { self.dialect = dialect self.mode = mode } enum Mode { case binded case plain } func string(from query: String, with formattedValues: [String]) -> String { switch mode { case .binded: return binded(query) case .plain: return plain(query: query, with: formattedValues) } } private func binded(_ query: String, _ valueRetriever: @escaping (Int) -> String) -> String { let rawChars: [Character] = Array(query) var finalChars: [Character] = [] var skipTill = -1 var b = 1 for (i, char) in rawChars.enumerated() { guard skipTill < i else { continue } guard char == dialect.bindSymbol.first else { finalChars.append(char) continue } if dialect.bindSymbol.count > 1 { guard rawChars.count >= i + dialect.bindSymbol.count else { continue } for n in 1...dialect.bindSymbol.count - 1 { guard rawChars[i + n] == Array(dialect.bindSymbol)[n] else { continue } } skipTill = i + dialect.bindSymbol.count - 1 } finalChars.append(contentsOf: Array(valueRetriever(b))) b = b + 1 } return String(finalChars) } private func binded(_ query: String) -> String { binded(query) { self.dialect.bindKey($0) } } private func plain(query: String, with formattedValues: [String]) -> String { binded(query) { formattedValues[$0 - 1] } } } extension String { fileprivate func split(by length: Int) -> [String] { var startIndex = self.startIndex var results = [Substring]() while startIndex < self.endIndex { let endIndex = self.index(startIndex, offsetBy: length, limitedBy: self.endIndex) ?? self.endIndex results.append(self[startIndex.. SwifQLable { build(.array_agg, body: aggregateExpression.parts) } /// `SELECT array_remove(ARRAY[1,2,3,2], 2);` will return {1,3} public static func array_remove(_ queryPart: SwifQLable...) -> SwifQLable { array_remove(queryPart) } /// `SELECT array_remove(ARRAY[1,2,3,2], 2);` will return {1,3} public static func array_remove(_ queryParts: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = [] for (i, q) in queryParts.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: q.parts) } return build(.array_remove, body: parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions+General.swift ================================================ // // Functions+General.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // extension Fn.Name { public static var substr: Self = .init("substr") public static var coalesce: Self = .init("coalesce") public static var octet_length: Self = .init("octet_length") public static var cast: Self = .init("cast") public static var ifnull: Self = .init("ifnull") public static var isnull: Self = .init("isnull") public static var nvl: Self = .init("nvl") public static var expression: Self = .init("expression") } extension Fn { public static func substr(_ queryPart: SwifQLable, _ to: Int) -> SwifQLable { var parts: [SwifQLPart] = queryPart.parts parts.append(o: .comma) parts.append(o: .space) parts.append(safe: to) return build(.substr, body: parts) } /// `SELECT COALESCE (NULL, 2 , 1);` will return 2 public static func coalesce(_ queryPart: SwifQLable...) -> SwifQLable { coalesce(queryPart) } /// `SELECT COALESCE (NULL, 2 , 1);` will return 2 public static func coalesce(_ queryParts: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = [] for (i, q) in queryParts.enumerated() { if i > 0 { parts.append(o: .comma) } parts.append(contentsOf: q.parts) } return build(.coalesce, body: parts) } public static func octet_length(_ string: SwifQLable) -> SwifQLable { build(.octet_length, body: string.parts) } public static func cast(_ queryPart: SwifQLable, _ to: Type) -> SwifQLable { cast(nil, queryPart, to) } public static func cast(_ from: Type?, _ queryPart: SwifQLable, _ to: Type) -> SwifQLable { var parts: [SwifQLPart] = [] if let from = from?.name { parts.append(o: .custom(from)) parts.append(o: .space) } parts.append(contentsOf: queryPart.parts) parts.append(o: .space) parts.append(o: .as) parts.append(o: .space) parts.append(o: .custom(to.name)) return build(.cast, body: parts) } public static func ifNull(_ value1: SwifQLable, _ value2: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = value1.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: value2.parts) return build(.ifnull, body: parts) } public static func isNull(_ value1: SwifQLable, _ value2: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = value1.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: value2.parts) return build(.isnull, body: parts) } public static func nvl(_ value1: SwifQLable, _ value2: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = value1.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: value2.parts) return build(.nvl, body: parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions+MySQL.swift ================================================ // // Functions+MySQL.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // extension Fn.Name { public static var from_unixtime: Self = .init("FROM_UNIXTIME") public static var date_format: Self = .init("DATE_FORMAT") } extension Fn { public static func from_unixtime(_ timeinterval: SwifQLable, _ format: String? = nil) -> SwifQLable { var parts: [SwifQLPart] = timeinterval.parts if let format = format { parts.append(o: .comma) parts.append(o: .space) parts.append(o: .custom(format.singleQuotted)) } return build(.from_unixtime, body: parts) } /// Formats the date value according to the format string. /// # Example /// ```swift /// Fn.date_format(\User.createdAt, "%y-%m") /// ``` /// # Result /// ``` /// date_format(User.createdAt, '%y-%m') /// ``` /// [Learn more →](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format) /// [Learn more →](https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-format) public static func date_format(_ datetime: SwifQLable, _ format: String) -> SwifQLable { var parts: [SwifQLPart] = datetime.parts parts.append(o: .comma) parts.append(o: .space) parts.append(o: .custom(format.singleQuotted)) return build(.date_format, body: parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions+Numeric.swift ================================================ // // Functions+Numeric.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // extension Fn.Name { public static var abs: Self = .init("abs") public static var avg: Self = .init("avg") public static var ceil: Self = .init("ceil") public static var ceiling: Self = .init("ceiling") public static var count: Self = .init("count") public static var div: Self = .init("div") public static var exp: Self = .init("exp") public static var floor: Self = .init("floor") public static var max: Self = .init("max") public static var min: Self = .init("min") public static var mod: Self = .init("mod") public static var power: Self = .init("power") public static var random: Self = .init("random") public static var round: Self = .init("round") public static var setseed: Self = .init("setseed") public static var sign: Self = .init("sign") public static var sqrt: Self = .init("sqrt") public static var sum: Self = .init("sum") } extension Fn { /// Returns the absolute value of a number /// [Learn more →](https://www.techonthenet.com/postgresql/functions/abs.php) public static func abs(_ number: SwifQLable) -> SwifQLable { build(.abs, body: number.parts) } /// Returns the average value of an expression /// [Learn more →](https://www.techonthenet.com/postgresql/functions/avg.php) public static func avg(_ quantity: SwifQLable) -> SwifQLable { build(.avg, body: quantity.parts) } /// Returns the smallest integer value that is greater than or equal to a number /// [Learn more →](https://www.techonthenet.com/postgresql/functions/ceil.php) public static func ceil(_ number: SwifQLable) -> SwifQLable { build(.ceil, body: number.parts) } /// Returns the smallest integer value that is greater than or equal to a number /// [Learn more →](https://www.techonthenet.com/postgresql/functions/ceiling.php) public static func ceiling(_ number: SwifQLable) -> SwifQLable { build(.ceiling, body: number.parts) } /// Returns the count of an expression /// [Learn more →](https://www.techonthenet.com/postgresql/functions/count.php) public static func count(_ expression: SwifQLable) -> SwifQLable { build(.count, body: expression.parts) } /// Used for integer division where n is divided by m and an integer value is returned /// [Learn more →](https://www.techonthenet.com/postgresql/functions/div.php) public static func div(_ n: SwifQLable, _ m: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = n.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: m.parts) return build(.div, body: parts) } /// Used for integer division where n is divided by m and an integer value is returned /// [Learn more →](https://www.techonthenet.com/postgresql/functions/exp.php) public static func exp(_ n: SwifQLable, _ m: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = n.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: m.parts) return build(.exp, body: parts) } /// Returns the largest integer value that is equal to or less than a number /// [Learn more →](https://www.techonthenet.com/postgresql/functions/floor.php) public static func floor(_ number: SwifQLable) -> SwifQLable { build(.floor, body: number.parts) } /// Returns the maximum value of an expression /// [Learn more →](https://www.techonthenet.com/postgresql/functions/max.php) public static func max(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.max, body: aggregateExpression.parts) } /// Returns the minimum value of an expression /// [Learn more →](https://www.techonthenet.com/postgresql/functions/min.php) public static func min(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.min, body: aggregateExpression.parts) } /// Returns the remainder of n divided by m /// [Learn more →](https://www.techonthenet.com/postgresql/functions/mod.php) public static func mod(_ n: SwifQLable, _ m: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = n.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: m.parts) return build(.mod, body: parts) } /// Returns m raised to the nth power /// [Learn more →](https://www.techonthenet.com/postgresql/functions/power.php) public static func power(_ n: SwifQLable, _ m: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = n.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: m.parts) return build(.power, body: parts) } /// Random function can be used to return a random number or a random number within a range /// [Learn more →](https://www.techonthenet.com/postgresql/functions/random.php) public static func random() -> SwifQLable { build(.random, body: []) } /// Returns a number rounded to a certain number of decimal places /// [Learn more →](https://www.techonthenet.com/postgresql/functions/round.php) public static func round(_ number: SwifQLable, _ decimalPlaces: Int? = nil) -> SwifQLable { var parts: [SwifQLPart] = number.parts if let decimalPlaces = decimalPlaces { parts.append(o: .comma) parts.append(o: .space) parts.append(safe: decimalPlaces) } return build(.round, body: parts) } /// Can be used to set a seed for the next time that you call the random function. /// If you do not call setseed, PostgreSQL will use its own seed value. /// This may or may not be truly random. /// [Learn more →](https://www.techonthenet.com/postgresql/functions/setseed.php) public static func setseed(_ number: SwifQLable) -> SwifQLable { build(.setseed, body: number.parts) } /// Returns a value indicating the sign of a number /// [Learn more →](https://www.techonthenet.com/postgresql/functions/sign.php) public static func sign(_ number: SwifQLable) -> SwifQLable { build(.sign, body: number.parts) } /// Returns the square root of a number /// [Learn more →](https://www.techonthenet.com/postgresql/functions/sqrt.php) public static func sqrt(_ number: SwifQLable) -> SwifQLable { build(.sqrt, body: number.parts) } /// Returns the summed value of an expression /// [Learn more →](https://www.techonthenet.com/postgresql/functions/sum.php) public static func sum(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.sum, body: aggregateExpression.parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions+PostgresBool.swift ================================================ // // Functions+PostgresBool.swift // SwifQL // // Created by Ethan Lozano on 06.12.20. // import Foundation extension Fn.Name { public static let bool_and: Self = .init("bool_and") public static let bool_or: Self = .init("bool_or") } extension Fn { public static func bool_and(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.bool_and, body: aggregateExpression.parts) } public static func bool_or(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.bool_or, body: aggregateExpression.parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions+PostgresJSON.swift ================================================ // // Functions+PostgresJSON.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // extension Fn.Name { public static var json_agg: Self = .init("json_agg") public static var to_json: Self = .init("to_json") public static var array_to_json: Self = .init("array_to_json") public static var row_to_json: Self = .init("row_to_json") public static var json_build_array: Self = .init("json_build_array") public static var json_build_object: Self = .init("json_build_object") public static var json_object: Self = .init("json_object") public static var json_array_length: Self = .init("json_array_length") public static var json_each: Self = .init("json_each") public static var json_each_text: Self = .init("json_each_text") public static var json_extract_path: Self = .init("json_extract_path") public static var json_extract_path_text: Self = .init("json_extract_path_text") public static var json_object_keys: Self = .init("json_object_keys") public static var json_populate_record: Self = .init("json_populate_record") public static var json_populate_recordset: Self = .init("json_populate_recordset") public static var json_array_elements: Self = .init("json_array_elements") public static var json_array_elements_text: Self = .init("json_array_elements_text") public static var json_typeof: Self = .init("json_typeof") public static var json_to_record: Self = .init("json_to_record") public static var json_to_recordset: Self = .init("json_to_recordset") public static var json_strip_nulls: Self = .init("json_strip_nulls") } extension Fn { /// public static func json_agg(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_agg, body: aggregateExpression.parts) } /// Returns the value as json. /// Arrays and composites are converted (recursively) to arrays and objects; /// otherwise, if there is a cast from the type to json, the cast function will be used to perform the conversion; /// otherwise, a scalar value is produced. /// For any scalar type other than a number, a Boolean, or a null value, /// the text representation will be used, in such a fashion that it is a valid json or jsonb value. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func to_json(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.to_json, body: aggregateExpression.parts) } /// Returns the array as a JSON array. /// A PostgreSQL multidimensional array becomes a JSON array of arrays. /// Line feeds will be added between dimension-1 elements if pretty_bool is true /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func array_to_json(_ anyarray: SwifQLable, pretty: Bool? = nil) -> SwifQLable { var parts: [SwifQLPart] = anyarray.parts if let pretty = pretty { parts.append(o: .comma) parts.append(o: .space) parts.append(safe: pretty) } return build(.array_to_json, body: parts) } /// Returns the row as a JSON object. /// Line feeds will be added between level-1 elements if pretty_bool is true /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func row_to_json(_ record: SwifQLable, pretty: Bool? = nil) -> SwifQLable { var parts: [SwifQLPart] = record.parts if let pretty = pretty { parts.append(o: .comma) parts.append(o: .space) parts.append(safe: pretty) } return build(.row_to_json, body: parts) } /// Builds a possibly-heterogeneously-typed JSON array out of a variadic argument list /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_build_array(_ items: SwifQLable...) -> SwifQLable { json_build_array(items) } /// Builds a possibly-heterogeneously-typed JSON array out of a variadic argument list /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_build_array(_ items: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = [] for (i, v) in items.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return build(.json_build_array, body: parts) } /// Builds a JSON object out of a variadic argument list. /// By convention, the argument list consists of alternating keys and values /// # Example /// ```swift /// Fn.json_build_object("foo", 1, "bar", 2) /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_build_object(_ items: SwifQLable...) -> SwifQLable { json_build_object(items) } /// Builds a JSON object out of a variadic argument list. /// By convention, the argument list consists of alternating keys and values /// # Example /// ```swift /// Fn.json_build_object("foo", 1, "bar", 2) /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_build_object(_ items: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = [] for (i, v) in items.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return build(.json_build_object, body: parts) } /// Builds a JSON object out of a text array. /// The array must have either exactly one dimension with an even number of members, /// in which case they are taken as alternating key/value pairs, /// or two dimensions such that each inner array has exactly two elements, which are taken as a key/value pair /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_object(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_object, body: aggregateExpression.parts) } /// This form of json_object takes keys and values pairwise from two separate arrays. /// In all other respects it is identical to the one-argument form. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_object(keys: SwifQLable, values: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = keys.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: values.parts) return build(.json_object, body: parts) } /// Returns the number of elements in the outermost JSON array /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_array_length(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_array_length, body: aggregateExpression.parts) } /// Expands the outermost JSON object into a set of key/value pairs /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_each(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_each, body: aggregateExpression.parts) } /// Expands the outermost JSON object into a set of key/value pairs. The returned values will be of type text /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_each_text(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_each_text, body: aggregateExpression.parts) } /// Returns JSON value pointed to by path_elems (equivalent to #> operator) /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_extract_path(_ from_json: SwifQLable, path_elems: [String]) -> SwifQLable { var parts: [SwifQLPart] = from_json.parts parts.append(o: .comma) parts.append(o: .space) for (i, v) in path_elems.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return build(.json_extract_path, body: parts) } public static func json_extract_path(_ from_json: SwifQLable, path_elems: String...) -> SwifQLable { json_extract_path(from_json, path_elems: path_elems) } /// Returns JSON value pointed to by path_elems as text (equivalent to #>> operator) /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_extract_path_text(_ from_json: SwifQLable, path_elems: [String]) -> SwifQLable { var parts: [SwifQLPart] = from_json.parts parts.append(o: .comma) parts.append(o: .space) for (i, v) in path_elems.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return build(.json_extract_path_text, body: parts) } public static func json_extract_path_text(_ from_json: SwifQLable, path_elems: String...) -> SwifQLable { json_extract_path_text(from_json, path_elems: path_elems) } /// Returns set of keys in the outermost JSON object. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_object_keys(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_object_keys, body: aggregateExpression.parts) } /// Expands the object in from_json to a row whose columns match the record type defined by base (see note below). /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_populate_record(base: SwifQLable, from_json: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = base.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: from_json.parts) return build(.json_populate_record, body: parts) } /// Expands the outermost array of objects in from_json to a set of rows whose columns match the record type defined by base (see note below). /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_populate_recordset(base: SwifQLable, from_json: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = base.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: from_json.parts) return build(.json_populate_recordset, body: parts) } /// Expands a JSON array to a set of JSON values. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_array_elements(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_array_elements, body: aggregateExpression.parts) } /// Expands a JSON array to a set of text values. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_array_elements_text(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_array_elements_text, body: aggregateExpression.parts) } /// Returns the type of the outermost JSON value as a text string. Possible types are object, array, string, number, boolean, and null. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_typeof(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_typeof, body: aggregateExpression.parts) } /// Builds an arbitrary record from a JSON object (see note below). As with all functions returning record, the caller must explicitly define the structure of the record with an AS clause. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_to_record(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_to_record, body: aggregateExpression.parts) } /// Builds an arbitrary set of records from a JSON array of objects (see note below). As with all functions returning record, the caller must explicitly define the structure of the record with an AS clause. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_to_recordset(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_to_recordset, body: aggregateExpression.parts) } /// Returns from_json with all object fields that have null values omitted. Other null values are untouched. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func json_strip_nulls(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.json_strip_nulls, body: aggregateExpression.parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions+PostgresJSONB.swift ================================================ // // Functions+PostgresJSONB.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // extension Fn.Name { public static var jsonb_agg: Self = .init("jsonb_agg") public static var to_jsonb: Self = .init("to_jsonb") public static var jsonb_build_array: Self = .init("jsonb_build_array") public static var jsonb_build_object: Self = .init("jsonb_build_object") public static var jsonb_object: Self = .init("jsonb_object") public static var jsonb_array_length: Self = .init("jsonb_array_length") public static var jsonb_each: Self = .init("jsonb_each") public static var jsonb_each_text: Self = .init("jsonb_each_text") public static var jsonb_extract_path: Self = .init("jsonb_extract_path") public static var jsonb_extract_path_text: Self = .init("jsonb_extract_path_text") public static var jsonb_object_keys: Self = .init("jsonb_object_keys") public static var jsonb_populate_record: Self = .init("jsonb_populate_record") public static var jsonb_populate_recordset: Self = .init("jsonb_populate_recordset") public static var jsonb_array_elements: Self = .init("jsonb_array_elements") public static var jsonb_array_elements_text: Self = .init("jsonb_array_elements_text") public static var jsonb_typeof: Self = .init("jsonb_typeof") public static var jsonb_to_record: Self = .init("jsonb_to_record") public static var jsonb_to_recordset: Self = .init("jsonb_to_recordset") public static var jsonb_strip_nulls: Self = .init("jsonb_strip_nulls") public static var jsonb_set: Self = .init("jsonb_set") public static var jsonb_insert: Self = .init("jsonb_insert") public static var jsonb_pretty: Self = .init("jsonb_pretty") } extension Fn { /// public static func jsonb_agg(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_agg, body: aggregateExpression.parts) } /// Returns the value as jsonb. /// Arrays and composites are converted (recursively) to arrays and objects; /// otherwise, if there is a cast from the type to json, the cast function will be used to perform the conversion; /// otherwise, a scalar value is produced. /// For any scalar type other than a number, a Boolean, or a null value, /// the text representation will be used, in such a fashion that it is a valid json or jsonb value. public static func to_jsonb(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.to_jsonb, body: aggregateExpression.parts) } /// Builds a possibly-heterogeneously-typed JSON array out of a variadic argument list /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_build_array(_ items: SwifQLable...) -> SwifQLable { jsonb_build_array(items) } /// Builds a possibly-heterogeneously-typed JSON array out of a variadic argument list /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_build_array(_ items: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = [] for (i, v) in items.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return build(.jsonb_build_array, body: parts) } /// Builds a JSON object out of a variadic argument list. /// By convention, the argument list consists of alternating keys and values /// # Example /// ```swift /// Fn.jsonb_build_object("foo", 1, "bar", 2) /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_build_object(_ items: SwifQLable...) -> SwifQLable { jsonb_build_object(items) } /// Builds a JSON object out of a variadic argument list. /// By convention, the argument list consists of alternating keys and values /// # Example /// ```swift /// Fn.jsonb_build_object("foo", 1, "bar", 2) /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_build_object(_ items: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = [] for (i, v) in items.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return build(.jsonb_build_object, body: parts) } /// Builds a JSON object out of a text array. /// The array must have either exactly one dimension with an even number of members, /// in which case they are taken as alternating key/value pairs, /// or two dimensions such that each inner array has exactly two elements, which are taken as a key/value pair /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_object(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_object, body: aggregateExpression.parts) } /// This form of json_object takes keys and values pairwise from two separate arrays. /// In all other respects it is identical to the one-argument form. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_object(keys: SwifQLable, values: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = keys.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: values.parts) return build(.jsonb_object, body: parts) } /// Returns the number of elements in the outermost JSON array /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_array_length(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_array_length, body: aggregateExpression.parts) } /// Expands the outermost JSON object into a set of key/value pairs /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_each(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_each, body: aggregateExpression.parts) } /// Expands the outermost JSON object into a set of key/value pairs. The returned values will be of type text /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_each_text(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_each_text, body: aggregateExpression.parts) } /// Returns JSON value pointed to by path_elems (equivalent to #> operator) /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_extract_path(_ from_json: SwifQLable, path_elems: [String]) -> SwifQLable { var parts: [SwifQLPart] = from_json.parts parts.append(o: .comma) parts.append(o: .space) for (i, v) in path_elems.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return build(.jsonb_extract_path, body: parts) } public static func jsonb_extract_path(_ from_json: SwifQLable, path_elems: String...) -> SwifQLable { jsonb_extract_path(from_json, path_elems: path_elems) } /// Returns JSON value pointed to by path_elems as text (equivalent to #>> operator) /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_extract_path_text(_ from_json: SwifQLable, path_elems: [String]) -> SwifQLable { var parts: [SwifQLPart] = from_json.parts parts.append(o: .comma) parts.append(o: .space) for (i, v) in path_elems.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return build(.jsonb_extract_path_text, body: parts) } public static func jsonb_extract_path_text(_ from_json: SwifQLable, path_elems: String...) -> SwifQLable { jsonb_extract_path_text(from_json, path_elems: path_elems) } /// Returns set of keys in the outermost JSON object. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_object_keys(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_object_keys, body: aggregateExpression.parts) } /// Expands the object in from_json to a row whose columns match the record type defined by base (see note below). /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_populate_record(base: SwifQLable, from_json: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = base.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: from_json.parts) return build(.jsonb_populate_record, body: parts) } /// Expands the outermost array of objects in from_json to a set of rows whose columns match the record type defined by base (see note below). /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_populate_recordset(base: SwifQLable, from_json: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = base.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: from_json.parts) return build(.jsonb_populate_recordset, body: parts) } /// Expands a JSON array to a set of JSON values. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_array_elements(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_array_elements, body: aggregateExpression.parts) } /// Expands a JSON array to a set of text values. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_array_elements_text(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_array_elements_text, body: aggregateExpression.parts) } /// Returns the type of the outermost JSON value as a text string. Possible types are object, array, string, number, boolean, and null. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_typeof(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_typeof, body: aggregateExpression.parts) } /// Builds an arbitrary record from a JSON object (see note below). As with all functions returning record, the caller must explicitly define the structure of the record with an AS clause. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_to_record(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_to_record, body: aggregateExpression.parts) } /// Builds an arbitrary set of records from a JSON array of objects (see note below). As with all functions returning record, the caller must explicitly define the structure of the record with an AS clause. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_to_recordset(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_to_recordset, body: aggregateExpression.parts) } /// Returns from_json with all object fields that have null values omitted. Other null values are untouched. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_strip_nulls(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_strip_nulls, body: aggregateExpression.parts) } /// Returns target with the section designated by path replaced by new_value, /// or with new_value added if create_missing is true ( default is true) /// and the item designated by path does not exist. /// As with the path orientated operators, negative integers /// that appear in path count from the end of JSON arrays. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) // public static func jsonb_set(target: SwifQLable, path text: [String], new_value: SwifQLable, create_missing: Bool? = nil) -> SwifQLable { // TDB // return _buildFn(.jsonb_set, body: aggregateExpression.parts) // } /// Returns target with new_value inserted. /// If target section designated by path is in a JSONB array, /// new_value will be inserted before target or after if insert_after is true (default is false). /// If target section designated by path is in JSONB object, new_value will be inserted /// only if target does not exist. As with the path orientated operators, negative integers /// that appear in path count from the end of JSON arrays. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) // public static func jsonb_insert(target: SwifQLable, path text: [String], new_value: SwifQLable, insert_after: Bool? = nil) -> SwifQLable { // TDB // return _buildFn(.jsonb_insert, body: aggregateExpression.parts) // } /// Returns from_json as indented JSON text. /// [Learn more →](https://www.postgresql.org/docs/current/functions-json.html) public static func jsonb_pretty(_ aggregateExpression: SwifQLable) -> SwifQLable { build(.jsonb_pretty, body: aggregateExpression.parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions+PostgresSeries.swift ================================================ // // Functions+PostgresSeries.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // extension Fn.Name { public static var generate_series: Self = .init("generate_series") } extension Fn { /// Generate series /// # Example /// ```swift /// Fn.generate_series(1, 4) /// Fn.generate_series(1, 4, 2) /// Fn.generate_series('2019-10-01', '2019-10-04', '1 day') /// ``` /// # Result /// ``` /// 1, 2, 3, 4 /// 1, 3 /// 2019-10-01, 2019-10-02, 2019-10-03, 2019-10-04 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-srf.html) public static func generate_series(_ start: SwifQLable, _ stop: SwifQLable, _ step: SwifQLable? = nil) -> SwifQLable { var parts: [SwifQLPart] = start.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: stop.parts) if let step = step { parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: step.parts) } return build(.generate_series, body: parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions+PostgresTime.swift ================================================ // // Functions+PostgresTime.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // extension Fn.Name { public static var age: Self = .init("age") public static var clock_timestamp: Self = .init("clock_timestamp") public static var current_date: Self = .init("current_date") public static var current_time: Self = .init("current_time") public static var current_timestamp: Self = .init("current_timestamp") public static var date_part: Self = .init("date_part") public static var date_trunc: Self = .init("date_trunc") public static var extract: Self = .init("extract") public static var isfinite: Self = .init("isfinite") public static var justify_days: Self = .init("justify_days") public static var justify_hours: Self = .init("justify_hours") public static var justify_interval: Self = .init("justify_interval") public static var localtime: Self = .init("localtime") public static var localtimestamp: Self = .init("localtimestamp") public static var make_date: Self = .init("make_date") public static var make_interval: Self = .init("make_interval") public static var make_time: Self = .init("make_time") public static var make_timestamp: Self = .init("make_timestamp") public static var make_timestamptz: Self = .init("make_timestamptz") public static var now: Self = .init("now") public static var statement_timestamp: Self = .init("statement_timestamp") public static var timeofday: Self = .init("timeofday") public static var transaction_timestamp: Self = .init("transaction_timestamp") public static var to_timestamp: Self = .init("to_timestamp") } extension Fn { /// Subtract arguments, producing a “symbolic” result that uses years and months, rather than just days /// # Example /// ```swift /// Fn.age("2001-04-10" => .timestamp, "1957-06-13" => .timestamp) /// ``` /// # Result /// ``` /// 43 years 9 mons 27 days /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func age(_ timestamp1: SwifQLable, _ timestamp2: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = timestamp1.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: timestamp2.parts) return build(.age, body: parts) } /// Subtract from current_date (at midnight) /// # Example /// ```swift /// Fn.age("2001-04-10" => .timestamp) /// ``` /// # Result /// ``` /// 43 years 8 mons 3 days /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func age(_ timestamp1: SwifQLable) -> SwifQLable { build(.age, body: timestamp1.parts) } /// Current date and time (changes during statement execution) /// # Example /// ```swift /// Fn.clock_timestamp() /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT) public static func clock_timestamp() -> SwifQLable { build(.clock_timestamp, body: []) } /// Current date /// # Example /// ```swift /// Fn.current_date /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT) public static var current_date: SwifQLable { SwifQLableParts(parts: Name.current_date.part) } /// Current time of day /// # Example /// ```swift /// Fn.current_time /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT) public static func current_time(_ aggregateExpression: SwifQLable) -> SwifQLable { SwifQLableParts(parts: Name.current_time.part) } /// Current date and time (start of current transaction) /// # Example /// ```swift /// Fn.current_timestamp /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT) public static func current_timestamp(_ aggregateExpression: SwifQLable) -> SwifQLable { SwifQLableParts(parts: Name.current_timestamp.part) } /// Get subfield (equivalent to extract) /// # Example with timestamp /// ```swift /// Fn.date_part("hour", "2001-02-16 20:38:40" => .timestamp) /// ``` /// # Result /// ``` /// 20 /// ``` /// # Example with interval /// ```swift /// Fn.date_part("month", "2 years 3 months" => .interval) /// ``` /// # Result /// ``` /// 3 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT) public static func date_part(_ text: SwifQLable, _ value: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = text.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: value.parts) return build(.date_part, body: parts) } /// Truncate to specified precision /// # Example with timestamp /// ```swift /// Fn.date_trunc("hour", "2001-02-16 20:38:40" => .timestamp) /// ``` /// # Result /// ``` /// 2001-02-16 20:00:00 /// ``` /// # Example with interval /// ```swift /// Fn.date_trunc("hour", "2 days 3 hours 40 minutes" => .interval) /// ``` /// # Result /// ``` /// 2 days 03:00:00 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC) public static func date_trunc(_ text: SwifQLable, _ value: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = text.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: value.parts) return build(.date_trunc, body: parts) } /// Get subfield /// # Example with timestamp /// ```swift /// Fn.extract(.hour, "2001-02-16 20:38:40" => .timestamp) /// ``` /// # Result /// ``` /// 20 /// ``` /// # Example with interval /// ```swift /// Fn.extract(.month, "2 years 3 months" => .interval) /// ``` /// # Result /// ``` /// 3 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT) public static func extract(_ field: ExtractFieldValue, from value: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(safe: field.value) parts.append(o: .space) parts.append(o: .from) parts.append(o: .space) parts.append(contentsOf: value.parts) return build(.extract, body: parts) } /// Get subfield /// # Example with timestamp /// ```swift /// Fn.extract("hour", "2001-02-16 20:38:40" => .timestamp) /// ``` /// # Result /// ``` /// 20 /// ``` /// # Example with interval /// ```swift /// Fn.extract("month", "2 years 3 months" => .interval) /// ``` /// # Result /// ``` /// 3 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT) public static func extract(_ field: SwifQLable, from value: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = field.parts parts.append(o: .space) parts.append(o: .from) parts.append(o: .space) parts.append(contentsOf: value.parts) return build(.extract, body: parts) } /// Test for finite date (not +/-infinity) /// # Example /// ```swift /// Fn.isfinite("4 hours" => .interval) /// ``` /// # Result /// ``` /// true /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func isfinite(_ interval: SwifQLable) -> SwifQLable { build(.isfinite, body: interval.parts) } /// Adjust interval so 30-day time periods are represented as months /// # Example /// ```swift /// Fn.justify_days("35 days" => .interval) /// ``` /// # Result /// ``` /// 1 mon 5 days /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func justify_days(_ interval: SwifQLable) -> SwifQLable { build(.justify_days, body: interval.parts) } /// Adjust interval so 24-hour time periods are represented as days /// # Example /// ```swift /// Fn.justify_hours("27 hours" => .interval) /// ``` /// # Result /// ``` /// 1 day 03:00:00 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func justify_hours(_ interval: SwifQLable) -> SwifQLable { build(.justify_hours, body: interval.parts) } /// Adjust interval using justify_days and justify_hours, with additional sign adjustments /// # Example /// ```swift /// Fn.justify_interval("1 mon -1 hour" => .interval) /// ``` /// # Result /// ``` /// 29 days 23:00:00 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func justify_interval(_ interval: SwifQLable) -> SwifQLable { build(.justify_interval, body: interval.parts) } /// Current time of day /// # Example /// ```swift /// Fn.localtime /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static var localtime: SwifQLable { SwifQLableParts(parts: Name.localtime.part) } /// Current date and time (start of current transaction) /// # Example /// ```swift /// Fn.localtimestamp /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static var localtimestamp: SwifQLable { SwifQLableParts(parts: Name.localtimestamp.part) } /// Create date from year, month and day fields /// # Example /// ```swift /// Fn.make_date(2013, 7, 15) /// ``` /// # Result /// ``` /// 2013-07-15 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func make_date(_ year: SwifQLable, _ month: SwifQLable, _ day: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = year.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: month.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: day.parts) return build(.make_date, body: parts) } /// Create interval from years, months, weeks, days, hours, minutes and seconds fields /// # Example /// ```swift /// Fn.make_interval(days: 10) /// ``` /// # Result /// ``` /// 10 days /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func make_interval(years: SwifQLable? = nil, months: SwifQLable? = nil, weeks: SwifQLable? = nil, days: SwifQLable? = nil, hours: SwifQLable? = nil, mins: SwifQLable? = nil, secs: SwifQLable? = nil) -> SwifQLable { var parts: [SwifQLPart] = [] if let years = years { parts.append(o: .custom("years => ")) if let number = years as? Int { parts.append(o: .custom("\(number)")) } else { parts.append(contentsOf: years.parts) } } if let months = months { if parts.count > 0 { parts.append(o: .comma, .space) } parts.append(o: .custom("months => ")) if let number = months as? Int { parts.append(o: .custom("\(number)")) } else { parts.append(contentsOf: months.parts) } } if let weeks = weeks { if parts.count > 0 { parts.append(o: .comma, .space) } parts.append(o: .custom("weeks => ")) if let number = weeks as? Int { parts.append(o: .custom("\(number)")) } else { parts.append(contentsOf: weeks.parts) } } if let days = days { if parts.count > 0 { parts.append(o: .comma, .space) } parts.append(o: .custom("days => ")) if let number = days as? Int { parts.append(o: .custom("\(number)")) } else { parts.append(contentsOf: days.parts) } } if let hours = hours { if parts.count > 0 { parts.append(o: .comma, .space) } parts.append(o: .custom("hours => ")) if let number = hours as? Int { parts.append(o: .custom("\(number)")) } else { parts.append(contentsOf: hours.parts) } } if let mins = mins { if parts.count > 0 { parts.append(o: .comma, .space) } parts.append(o: .custom("mins => ")) if let number = mins as? Int { parts.append(o: .custom("\(number)")) } else { parts.append(contentsOf: mins.parts) } } if let secs = secs { if parts.count > 0 { parts.append(o: .comma, .space) } parts.append(o: .custom("secs => ")) if let number = secs as? Int { parts.append(o: .custom("\(number)")) } else if let number = secs as? Double { parts.append(o: .custom("\(number)")) } else { parts.append(contentsOf: secs.parts) } } return build(.make_interval, body: parts) } /// Create time from hour, minute and seconds fields /// # Example /// ```swift /// Fn.make_time(8, 15, 23.5) /// ``` /// # Result /// ``` /// 08:15:23.5 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func make_time(_ hour: SwifQLable, _ min: SwifQLable, _ sec: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = hour.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: min.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: sec.parts) return build(.make_time, body: parts) } /// Create timestamp from year, month, day, hour, minute and seconds fields /// # Example /// ```swift /// Fn.make_timestamp(2013, 7, 15, 8, 15, 23.5) /// ``` /// # Result /// ``` /// 2013-07-15 08:15:23.5 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func make_timestamp(_ year: SwifQLable, _ month: SwifQLable, _ day: SwifQLable, _ hour: SwifQLable, _ min: SwifQLable, _ sec: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = year.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: month.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: day.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: hour.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: min.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: sec.parts) return build(.make_timestamp, body: parts) } /// Create timestamp with time zone from year, month, day, hour, minute and seconds fields; /// if timezone is not specified, the current time zone is used /// # Example /// ```swift /// Fn.make_timestamptz(2013, 7, 15, 8, 15, 23.5) /// ``` /// # Result /// ``` /// 2013-07-15 08:15:23.5+01 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func make_timestamptz(_ year: SwifQLable, _ month: SwifQLable, _ day: SwifQLable, _ hour: SwifQLable, _ min: SwifQLable, _ sec: SwifQLable, _ timezone: SwifQLable? = nil) -> SwifQLable { var parts: [SwifQLPart] = year.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: month.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: day.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: hour.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: min.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: sec.parts) if let timezone = timezone { parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: timezone.parts) } return build(.make_timestamptz, body: parts) } /// Current date and time (start of current transaction) /// # Example /// ```swift /// Fn.now() /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func now() -> SwifQLable { build(.now, body: []) } /// Current date and time (start of current statement) /// # Example /// ```swift /// Fn.statement_timestamp() /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func statement_timestamp() -> SwifQLable { build(.statement_timestamp, body: []) } /// Current date and time (like clock_timestamp, but as a text string) /// # Example /// ```swift /// Fn.timeofday() /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func timeofday() -> SwifQLable { build(.timeofday, body: []) } /// Current date and time (start of current transaction) /// # Example /// ```swift /// Fn.transaction_timestamp() /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func transaction_timestamp() -> SwifQLable { build(.transaction_timestamp, body: []) } /// Convert Unix epoch (seconds since 1970-01-01 00:00:00+00) to timestamp /// # Example /// ```swift /// Fn.to_timestamp(1284352323) /// ``` /// # Result /// ``` /// 2010-09-13 04:32:03+00 /// ``` /// /// [Learn more →](https://www.postgresql.org/docs/11/functions-datetime.html) public static func to_timestamp(_ value: SwifQLable) -> SwifQLable { build(.to_timestamp, body: value.parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions+String.swift ================================================ // // Functions+String.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // extension Fn.Name { public static var bit_length: Self = .init("bit_length") public static var btrim: Self = .init("btrim") public static var char_length: Self = .init("char_length") public static var character_length: Self = .init("character_length") public static var initcap: Self = .init("initcap") public static var concat: Self = .init("concat") public static var concat_ws: Self = .init("concat_ws") public static var array_length: Self = .init("array_length") public static var length: Self = .init("length") public static var lower: Self = .init("lower") public static var lpad: Self = .init("lpad") public static var ltrim: Self = .init("ltrim") public static var position: Self = .init("position") public static var `repeat`: Self = .init("repeat") public static var replace: Self = .init("replace") public static var rpad: Self = .init("rpad") public static var rtrim: Self = .init("rtrim") public static var strpos: Self = .init("strpos") public static var substring: Self = .init("substring") public static var translate: Self = .init("translate") public static var trim: Self = .init("trim") public static var upper: Self = .init("upper") public static var string_agg: Self = .init("string_agg") public static var regexp_replace: Self = .init("regexp_replace") } extension Fn { /// String and non-string concatenation /// e.g. `'Post' || 'greSQL'` will return `PostgreSQL` /// so in Swift you can write it like `"Post" || "greSQL"` /// or using KeyPath like \User.firstName || " " || \User.lastName /// and KeyPath alias like u+\.firstName || " " || u+\.lastName /// [Learn more →](https://www.techonthenet.com/postgresql/functions/concat2.php) public static func concatStrings(lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = lhs.parts parts.append(o: .space) parts.append(o: .custom("||")) parts.append(o: .space) parts.append(contentsOf: rhs.parts) return SwifQLableParts(parts: parts) } /// Number of bits in string /// e.g. `bit_length('jose')` will return 32 /// [Learn more →]() public static func bit_length(_ string: SwifQLable) -> SwifQLable { build(.bit_length, body: string.parts) } /// Removes all specified characters from both the beginning and the end of a string /// [Learn more →](https://www.techonthenet.com/postgresql/functions/btrim.php) public static func btrim(_ string: SwifQLable, _ characters: String) -> SwifQLable { var parts: [SwifQLPart] = string.parts parts.append(o: .comma) parts.append(o: .space) parts.append(safe: characters) return build(.btrim, body: parts) } /// Returns the length of the specified string /// [Learn more →](https://www.techonthenet.com/postgresql/functions/char_length.php) public static func char_length(_ string: SwifQLable) -> SwifQLable { build(.char_length, body: string.parts) } /// Returns the length of the specified string /// [Learn more →](https://www.techonthenet.com/postgresql/functions/character_length.php) public static func character_length(_ string: SwifQLable) -> SwifQLable { build(.character_length, body: string.parts) } /// Converts the first letter of each word to uppercase and all other letters are converted to lowercase /// [Learn more →](https://www.techonthenet.com/postgresql/functions/initcap.php) public static func initcap(_ string: SwifQLable) -> SwifQLable { build(.initcap, body: string.parts) } /// Concatenate all arguments. NULL arguments are ignored. /// /// # Example /// ```swift /// Fn.concat("Hello ", \User.name) /// ``` /// # Result /// ``` /// concat('Hello ', "User"."name") /// ``` /// [Learn more →](https://www.postgresql.org/docs/9.1/functions-string.html) public static func concat(_ values: SwifQLable...) -> SwifQLable { concat(values) } /// Concatenate all arguments. NULL arguments are ignored. /// /// # Example /// ```swift /// Fn.concat("Hello ", \User.name) /// ``` /// # Result /// ``` /// concat('Hello ', "User"."name") /// ``` /// [Learn more →](https://www.postgresql.org/docs/9.1/functions-string.html) public static func concat(_ values: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = [] values.enumerated().forEach { offset, value in parts.append(contentsOf: value.parts) if offset < values.count - 1 { parts.append(o: .comma) parts.append(o: .space) } } return build(.concat, body: parts) } /// Concatenate all arguments. NULL arguments are ignored. /// /// # Example /// ```swift /// Fn.concat_ws(", ", "Hello", \User.name) /// ``` /// # Result /// ``` /// concat_ws(', ', 'Hello', "User"."name") /// ``` /// [Learn more →](https://www.postgresql.org/docs/9.1/functions-string.html) public static func concat_ws(_ values: SwifQLable...) -> SwifQLable { concat_ws(values) } /// Concatenate all arguments. NULL arguments are ignored. /// /// # Example /// ```swift /// Fn.concat_ws(", ", "Hello", \User.name) /// ``` /// # Result /// ``` /// concat_ws(', ', 'Hello', "User"."name") /// ``` /// [Learn more →](https://www.postgresql.org/docs/9.1/functions-string.html) public static func concat_ws(_ values: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = [] values.enumerated().forEach { offset, value in parts.append(contentsOf: value.parts) if offset < values.count - 1 { parts.append(o: .comma) parts.append(o: .space) } } return build(.concat_ws, body: parts) } /// Returns the length of the requested array dimension public static func array_length(_ anyArray: SwifQLable, _ dimension: Int = 1) -> SwifQLable { var parts: [SwifQLPart] = anyArray.parts parts.append(o: .comma) parts.append(o: .space) parts.append(safe: dimension) return build(.array_length, body: parts) } /// Returns the length of the specified string, expressed as the number of characters. /// [Learn more →](https://www.techonthenet.com/postgresql/functions/length.php) public static func length(_ string: SwifQLable) -> SwifQLable { build(.length, body: string.parts) } /// Converts all characters in the specified string to lowercase /// [Learn more →](https://www.techonthenet.com/postgresql/functions/lower.php) public static func lower(_ string: SwifQLable) -> SwifQLable { build(.lower, body: string.parts) } /// Returns a string that is left-padded with a specified string to a certain length /// [Learn more →](https://www.techonthenet.com/postgresql/functions/lpad.php) public static func lpad(_ string: SwifQLable, _ length: Int, _ padString: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = string.parts parts.append(o: .comma) parts.append(o: .space) parts.append(safe: length) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: padString.parts) return build(.lpad, body: parts) } /// Removes all specified characters from the left-hand side of a string /// [Learn more →](https://www.techonthenet.com/postgresql/functions/ltrim.php) public static func ltrim(_ string: SwifQLable, _ characters: String) -> SwifQLable { var parts: [SwifQLPart] = string.parts parts.append(o: .comma) parts.append(o: .space) parts.append(safe: characters) return build(.ltrim, body: parts) } /// Returns the location of a substring in a string /// [Learn more →](https://www.techonthenet.com/postgresql/functions/position.php) public static func position(_ substring: SwifQLable, in string: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = substring.parts parts.append(o: .space) parts.append(o: .in) parts.append(o: .space) parts.append(contentsOf: string.parts) return build(.position, body: parts) } /// Repeats a string a specified number of times /// [Learn more →](https://www.techonthenet.com/postgresql/functions/repeat.php) public static func `repeat`(_ string: SwifQLable, _ number: Int) -> SwifQLable { var parts: [SwifQLPart] = string.parts parts.append(o: .comma) parts.append(o: .space) parts.append(safe: number) return build(.repeat, body: parts) } /// Replaces all occurrences of a specified string /// [Learn more →](https://www.techonthenet.com/postgresql/functions/replace.php) public static func replace(_ string: SwifQLable, _ fromSubstring: SwifQLable, _ toSubstring: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = string.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: fromSubstring.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: toSubstring.parts) return build(.replace, body: parts) } /// Returns a string that is right-padded with a specified string to a certain length /// [Learn more →](https://www.techonthenet.com/postgresql/functions/rpad.php) public static func rpad(_ string: SwifQLable, _ length: Int, _ padString: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = string.parts parts.append(o: .comma) parts.append(o: .space) parts.append(safe: length) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: padString.parts) return build(.rpad, body: parts) } /// Removes all specified characters from the right-hand side of a string /// [Learn more →](https://www.techonthenet.com/postgresql/functions/rtrim.php) public static func rtrim(_ string: SwifQLable, _ characters: String) -> SwifQLable { var parts: [SwifQLPart] = string.parts parts.append(o: .comma) parts.append(o: .space) parts.append(safe: characters) return build(.rtrim, body: parts) } /// Returns the location of a substring in a string /// [Learn more →](https://www.techonthenet.com/postgresql/functions/strpos.php) public static func strpos(_ string: SwifQLable, _ substring: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = string.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: substring.parts) return build(.strpos, body: parts) } /// Allows you to extract a substring from a string /// [Learn more →](https://www.techonthenet.com/postgresql/functions/substring.php) public static func substring(_ string: SwifQLable, from startPosition: Int) -> SwifQLable { _substring(string, from: startPosition) } /// Allows you to extract a substring from a string /// [Learn more →](https://www.techonthenet.com/postgresql/functions/substring.php) public static func substring(_ string: SwifQLable, for length: Int) -> SwifQLable { _substring(string, for: length) } /// Allows you to extract a substring from a string /// [Learn more →](https://www.techonthenet.com/postgresql/functions/substring.php) public static func substring(_ string: SwifQLable, from startPosition: Int, for length: Int) -> SwifQLable { _substring(string, from: startPosition, for: length) } private static func _substring(_ string: SwifQLable, from startPosition: Int? = nil, for length: Int? = nil) -> SwifQLable { var parts: [SwifQLPart] = string.parts if let startPosition = startPosition { parts.append(o: .space) parts.append(o: .from) parts.append(o: .space) parts.append(safe: startPosition) } if let length = length { parts.append(o: .space) parts.append(o: .for) parts.append(o: .space) parts.append(safe: length) } return build(.substring, body: parts) } /// Replaces a sequence of characters in a string with another set of characters. However, it replaces a single character at a time. /// [Learn more →](https://www.techonthenet.com/postgresql/functions/translate.php) public static func translate(_ string: SwifQLable, _ stringToReplace: SwifQLable, _ replacementString: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = string.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: stringToReplace.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: replacementString.parts) return build(.translate, body: parts) } /// Removes all specified characters either from the beginning or the end of a string. /// [Learn more →](https://www.techonthenet.com/postgresql/functions/trim.php) public static func trim(_ string: SwifQLable) -> SwifQLable { build(.trim, body: string.parts) } /// Removes all specified characters either from the beginning or the end of a string. /// [Learn more →](https://www.techonthenet.com/postgresql/functions/trim.php) public static func trim(leading trimCharacter: SwifQLable? = nil, from string: SwifQLable) -> SwifQLable { _trim("leading", trimCharacter, from: string) } /// Removes all specified characters either from the beginning or the end of a string. /// [Learn more →](https://www.techonthenet.com/postgresql/functions/trim.php) public static func trim(trailing trimCharacter: SwifQLable? = nil, from string: SwifQLable) -> SwifQLable { _trim("trailing", trimCharacter, from: string) } /// Removes all specified characters either from the beginning or the end of a string. /// [Learn more →](https://www.techonthenet.com/postgresql/functions/trim.php) public static func trim(both trimCharacter: SwifQLable? = nil, from string: SwifQLable) -> SwifQLable { _trim("both", trimCharacter, from: string) } /// Private `trim` builder method private static func _trim(_ type: String, _ trimCharacter: SwifQLable? = nil, from string: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(o: .custom(type)) if let trimCharacter = trimCharacter { parts.append(o: .space) parts.append(contentsOf: trimCharacter.parts) } parts.append(o: .space) parts.append(o: .from) parts.append(o: .space) parts.append(contentsOf: string.parts) return build(.trim, body: string.parts) } /// Converts all characters in the specified string to uppercase /// [Learn more →](https://www.techonthenet.com/postgresql/functions/upper.php) public static func upper(_ string: SwifQLable) -> SwifQLable { build(.upper, body: string.parts) } /// Concatenates non-null input values into a string, separated by delimiter /// [Learn more →](https://www.postgresql.org/docs/11/functions-aggregate.html) public static func string_agg(_ string: SwifQLable, _ separator: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(contentsOf: string.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: separator.parts) return build(.string_agg, body: parts) } /// Replaces a sequence of characters in a string with another set of characters using regular expression pattern matching /// [Learn more →](https://www.techonthenet.com/oracle/functions/regexp_replace.php) public static func regexp_replace(_ string: SwifQLable, _ fromRegexp: SwifQLable, _ toSubstring: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(contentsOf: string.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: fromRegexp.parts) parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: toSubstring.parts) return build(.regexp_replace, body: parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions+TextSearch.swift ================================================ // // Functions+TextSearch.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // extension Fn.Name { public static var to_tsvector: Self = .init("to_tsvector") public static var to_tsquery: Self = .init("to_tsquery") public static var plainto_tsquery: Self = .init("plainto_tsquery") public static var ts_rank_cd: Self = .init("ts_rank_cd") } extension Fn { /// PostgreSQL provides the function to_tsvector for converting a document to the tsvector data type. /// to_tsvector([ config regconfig, ] document text) returns tsvector /// # Example /// ```swift /// Fn.to_tsvector("english", "a fat cat sat on a mat - it ate a fat rats") /// ``` /// # Result /// ``` /// 'ate':9 'cat':3 'fat':2,11 'mat':7 'rat':12 'sat':4 /// ``` /// [Learn more →](https://www.postgresql.org/docs/9.1/textsearch-controls.html) public static func to_tsvector(_ config: SwifQLable, _ text: SwifQLable? = nil) -> SwifQLable { var parts: [SwifQLPart] = config.parts if let text = text { parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: text.parts) } return build(.to_tsvector, body: parts) } /// PostgreSQL provides to_tsquery function for converting a query to the tsquery data type. /// to_tsquery offers access to more features than plainto_tsquery, but is less forgiving about its input. /// to_tsquery([ config regconfig, ] querytext text) returns tsquery /// # Example /// ```swift /// Fn.to_tsquery("english", "The & Fat & Rats") /// ``` /// # Result /// ``` /// 'fat' & 'rat' /// ``` /// [Learn more →](https://www.postgresql.org/docs/9.1/textsearch-controls.html) public static func to_tsquery(_ config: SwifQLable, _ text: SwifQLable? = nil) -> SwifQLable { var parts: [SwifQLPart] = config.parts if let text = text { parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: text.parts) } return build(.to_tsquery, body: parts) } /// `plainto_tsquery` transforms unformatted text querytext to tsquery /// plainto_tsquery([ config regconfig, ] querytext text) returns tsquery /// # Example /// ```swift /// Fn.plainto_tsquery("english", "The Fat Rats") /// ``` /// # Result /// ``` /// 'fat' & 'rat' /// ``` /// [Learn more →](https://www.postgresql.org/docs/9.1/textsearch-controls.html) public static func plainto_tsquery(_ config: SwifQLable, _ text: SwifQLable? = nil) -> SwifQLable { var parts: [SwifQLPart] = config.parts if let text = text { parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: text.parts) } return build(.plainto_tsquery, body: parts) } /// PostgreSQL provides two predefined ranking functions, which take into account lexical, proximity, /// and structural information; that is, they consider how often the query terms appear in the document, /// how close together the terms are in the document, and how important is the part of the document where they occur. /// `ts_rank_rd` calculates the rank of the provided tsquery /// ts_rank_rd(vector tsvector, query tsquery [, normalization integer ]) returns tsquery /// # Example /// ```swift /// Fn.ts_rank_cd("rats", Fn.to_tsquery("The Fat Rats")) /// ``` /// # Result /// ``` /// ts_rank_cd("rats", to_tsquery('The Fat Rats')) /// ``` /// [Learn more →](https://www.postgresql.org/docs/9.6/textsearch-controls.html#TEXTSEARCH-RANKING) public static func ts_rank_cd(_ vector: SwifQLable, _ query: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = vector.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: query.parts) return build(.ts_rank_cd, body: parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions+Window.swift ================================================ // // Functions+Window.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // extension Fn.Name { public static var row_number: Self = .init("row_number") public static var rank: Self = .init("rank") public static var dense_rank: Self = .init("dense_rank") public static var percent_rank: Self = .init("percent_rank") public static var cume_dist: Self = .init("cume_dist") public static var ntile: Self = .init("ntile") public static var lag: Self = .init("lag") public static var lead: Self = .init("lead") public static var first_value: Self = .init("first_value") public static var last_value: Self = .init("last_value") public static var nth_value: Self = .init("nth_value") } extension Fn { /// Number of the current row within its partition, counting from 1 /// /// # Example /// ```swift /// Fn.row_number() /// ``` /// # Result /// ``` /// row_number() /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-window.html) public static func row_number() -> SwifQLable { build(.row_number, body: []) } /// Rank of the current row with gaps /// same as row_number of its first peer /// /// # Example /// ```swift /// Fn.rank() /// ``` /// # Result /// ``` /// rank() /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-window.html) public static func rank() -> SwifQLable { build(.rank, body: []) } /// Rank of the current row without gaps /// this function counts peer groups /// /// # Example /// ```swift /// Fn.dense_rank() /// ``` /// # Result /// ``` /// dense_rank() /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-window.html) public static func dense_rank() -> SwifQLable { build(.dense_rank, body: []) } /// Relative rank of the current row: (rank - 1) / (total partition rows - 1) /// /// # Example /// ```swift /// Fn.percent_rank() /// ``` /// # Result /// ``` /// percent_rank() /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-window.html) public static func percent_rank() -> SwifQLable { build(.percent_rank, body: []) } /// Cumulative distribution: (number of partition rows preceding or peer with current row) / total partition rows /// /// # Example /// ```swift /// Fn.cume_dist() /// ``` /// # Result /// ``` /// cume_dist() /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-window.html) public static func cume_dist() -> SwifQLable { build(.cume_dist, body: []) } /// Integer ranging from 1 to the argument value, dividing the partition as equally as possible /// /// # Example /// ```swift /// Fn.ntile() /// ``` /// # Result /// ``` /// ntile() /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-window.html) public static func ntile(_ num_buckets: SwifQLable) -> SwifQLable { build(.ntile, body: num_buckets.parts) } /// Returns `value` evaluated at the row that is `offset` rows before the current row within the partition /// if there is no such row, instead return `default` (which must be of the same type as `value`). /// Both `offset` and `default` are evaluated with respect to the current row. /// If omitted, `offset` defaults to 1 and `default` to null /// /// # Example /// ```swift /// Fn.lag() /// ``` /// # Result /// ``` /// lag() /// ``` /// [Examples →](https://www.postgresqltutorial.com/postgresql-lag-function/) /// /// [Learn more →](https://www.postgresql.org/docs/current/functions-window.html) public static func lag(_ value: SwifQLable, _ offset: SwifQLable? = nil) -> SwifQLable { var parts: [SwifQLPart] = value.parts if let offset = offset { parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: offset.parts) } return build(.lag, body: parts) } /// Returns value evaluated at the row that is `offset` rows after the current row within the partition /// if there is no such row, instead return `default` (which must be of the same type as `value`). /// Both `offset` and `default` are evaluated with respect to the current row. /// If omitted, `offset` defaults to 1 and `default` to null /// /// # Example /// ```swift /// Fn.lead() /// ``` /// # Result /// ``` /// lead() /// ``` /// [Examples →](https://www.postgresqltutorial.com/postgresql-lead-function/) /// /// [Learn more →](https://www.postgresql.org/docs/current/functions-window.html) public static func lead(_ value: SwifQLable, _ offset: SwifQLable? = nil) -> SwifQLable { var parts: [SwifQLPart] = value.parts if let offset = offset { parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: offset.parts) } return build(.lead, body: parts) } /// Returns value evaluated at the row that is the first row of the window frame /// /// # Example /// ```swift /// Fn.first_value() /// ``` /// # Result /// ``` /// first_value() /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-window.html) public static func first_value(_ value: SwifQLable) -> SwifQLable { build(.first_value, body: value.parts) } /// Returns value evaluated at the row that is the last row of the window frame /// /// # Example /// ```swift /// Fn.last_value() /// ``` /// # Result /// ``` /// last_value() /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-window.html) public static func last_value(_ value: SwifQLable) -> SwifQLable { build(.last_value, body: value.parts) } /// Returns `value` evaluated at the row that is the `nth` row of the window frame (counting from 1) /// null if no such row /// /// # Example /// ```swift /// Fn.nth_value() /// ``` /// # Result /// ``` /// nth_value() /// ``` /// [Learn more →](https://www.postgresql.org/docs/current/functions-window.html) public static func nth_value(_ value: SwifQLable, _ nth: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = value.parts parts.append(o: .comma) parts.append(o: .space) parts.append(contentsOf: nth.parts) return build(.nth_value, body: parts) } } ================================================ FILE: Sources/SwifQL/Functions/Functions.swift ================================================ // // Functions.swift // SwifQL // // Created by Mihael Isaev on 04/11/2018. // import Foundation public struct Fn {} extension Fn { public struct Name: Sendable { let name: String public init (_ name: String) { self.name = name } public static func custom(_ name: String) -> Name { .init(name) } var part: SwifQLPartOperator { .init(name) } } } //MARK: Function builders extension Fn { public static func build(_ fn: Name) -> SwifQLable { build(fn, body: nil) } public static func build(_ fn: Name, body: SwifQLPart...) -> SwifQLable { build(fn, body: body) } public static func build(_ fn: Name, body: [SwifQLPart]? = nil) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(f: fn) if let body = body { parts.append(o: .openBracket) parts.append(contentsOf: body) parts.append(o: .closeBracket) } return SwifQLableParts(parts: parts) } } public func Select(_ queryPart: SwifQLable...) -> SwifQLable { Select(queryPart) } public func Select(_ queryParts: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(o: .select) parts.append(o: .space) for (i, q) in queryParts.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: q.parts) } return SwifQLableParts(parts: parts) } public var Select: SwifQLable { Fn.build(.custom("SELECT")) } //MARK: [SwifQLPart] extension extension Array where Element == SwifQLPart { public mutating func appendSpaceIfNeeded() { if count == 0 { return } if let last = last as? SwifQLPartOperator, last._value == " " { return } append(o: .space) } public mutating func append(f: Fn.Name) { append(f.part) } public mutating func append(o: SwifQLPartOperator...) { o.forEach { append($0) } } public mutating func append(h: SwifQLHybridOperator...) { h.forEach { append($0) } } public mutating func append(safe value: Any) { append(SwifQLPartSafeValue(value)) } } ================================================ FILE: Sources/SwifQL/HybridOperator.swift ================================================ // // HybridOperator.swift // // // Created by TierraCero on 5/30/23. // import Foundation extension SwifQLHybridOperator { public typealias HybridResult = SwifQLHybridOperator public static var random: HybridResult { .init("random()".operator, "rand()".operator) } private func concatWith(_ hybrid: HybridResult) -> HybridResult { return hybrid } } extension String { fileprivate var `operator`: SwifQLPartOperator { .init(self) } } ================================================ FILE: Sources/SwifQL/IndexItem.swift ================================================ // // IndexItem.swift // SwifQL // // Created by Mihael Isaev on 18.08.2020. // public class IndexItem: SwifQLable { public var parts: [SwifQLPart] { var parts: [SwifQLPart] = [] switch item { case .column(let name): parts.append(SwifQLPartColumn(name)) case .expression(let expression): parts.append(o: .openBracket) parts.append(contentsOf: expression.parts) parts.append(o: .closeBracket) } if order.parts.count > 0 { parts.append(o: .space) parts.append(contentsOf: order.parts) } return parts } public enum Order: SwifQLable { public var parts: [SwifQLPart] { var parts: [SwifQLPart] = [] switch self { case .asc: break case .ascNullsFirst: parts.append(o: .nulls) parts.append(o: .space) parts.append(o: .first) case .desc: parts.append(o: .desc) case .descNullsLast: parts.append(o: .desc) parts.append(o: .space) parts.append(o: .nulls) parts.append(o: .space) parts.append(o: .last) } return parts } case asc, ascNullsFirst, desc, descNullsLast } public enum Item { case column(String) case expression(SwifQLable) } let order: Order let item: Item public init (item: Item, order: Order) { self.item = item self.order = order } public static func column(_ column: String, order: Order = .asc) -> IndexItem { .init(item: .column(column), order: order) } public static func expression(_ expression: SwifQLable, order: Order = .asc) -> IndexItem { .init(item: .expression(expression), order: order) } } ================================================ FILE: Sources/SwifQL/IndexType.swift ================================================ // // IndexType.swift // SwifQL // // Created by Mihael Isaev on 18.08.2020. // import Foundation public class IndexType: SwifQLable { public var parts: [SwifQLPart] { var parts: [SwifQLPart] = [] parts.append(o: .custom(name)) return parts } let name: String public init (name: String) { self.name = name } public static var btree: IndexType { .init(name: "BTREE") } public static var hash: IndexType { .init(name: "HASH") } public static var gist: IndexType { .init(name: "GIST") } public static var gin: IndexType { .init(name: "GIN") } public static var spgist: IndexType { .init(name: "SPGIST") } public static var brin: IndexType { .init(name: "BRIN") } } ================================================ FILE: Sources/SwifQL/KeyPath.swift ================================================ // // KeyPath.swift // SwifQL // // Created by Mihael Isaev on 05/11/2018. // import Foundation public protocol KeyPathLastPath { var lastPath: String { get } } extension String: KeyPathLastPath { public var lastPath: String { self } } public protocol SwifQLUniversalKeyPathSimple: KeyPathLastPath { var path: String { get } var lastPath: String { get } } public protocol SwifQLUniversalKeyPath { associatedtype AType associatedtype AModel: Decodable associatedtype ARoot var path: String { get } var lastPath: String { get } var originalKeyPath: KeyPath { get } } //MARK: - Casting infix operator => : AdditionPrecedence /// e.g. `"1"::.text` public func => (lhs: SwifQLable, rhs: Type) -> SwifQLable { var parts: [SwifQLPart] = lhs.parts parts.append(o: .custom("::")) parts.append(o: .custom(rhs.name)) return SwifQLableParts(parts: parts) } /// e.g. `"hello" as "title"` public func => (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = lhs.parts parts.append(o: .space) parts.append(o: .as) parts.append(o: .space) if let rhs = rhs as? SwifQLUniversalKeyPathSimple { parts.append(SwifQLPartAlias(rhs.lastPath)) } else if let _ = rhs as? _AliasKeyPath { parts.append(contentsOf: rhs.parts) } else if let _ = rhs as? TableAlias { parts.append(contentsOf: rhs.parts) } else if let schemaWithTable = rhs as? Path.SchemaWithTable { parts.append(SwifQLPartAlias(schemaWithTable.table)) } else if let table = rhs as? Path.Table { parts.append(SwifQLPartAlias(table.name)) } else if let kp = rhs as? Keypathable { parts.append(SwifQLPartAlias(kp.lastPath)) } else { parts.append(SwifQLPartAlias(String(describing: rhs))) } return SwifQLableParts(parts: parts) } prefix operator => /// write `=>"aliasName"` in Swift /// to reach `"aliasName"` in SQL public prefix func => (rhs: String) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(SwifQLPartAlias(rhs)) return SwifQLableParts(parts: parts) } //MARK: - Basic arithmetic functions public func + (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = lhs.parts parts.append(o: .space) parts.append(o: .custom("+")) parts.append(o: .space) parts.append(contentsOf: rhs.parts) return SwifQLableParts(parts: parts) } infix operator ++: AdditionPrecedence public func ++ (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = lhs.parts parts.append(o: .space) parts.append(o: .custom("+")) parts.append(o: .space) parts.append(contentsOf: rhs.parts) return SwifQLableParts(parts: parts) } public func - (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = lhs.parts parts.append(o: .space) parts.append(o: .custom("-")) parts.append(o: .space) parts.append(contentsOf: rhs.parts) return SwifQLableParts(parts: parts) } infix operator --: AdditionPrecedence public func -- (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = lhs.parts parts.append(o: .space) parts.append(o: .custom("-")) parts.append(o: .space) parts.append(contentsOf: rhs.parts) return SwifQLableParts(parts: parts) } public func * (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = lhs.parts parts.append(o: .space) parts.append(o: .custom("*")) parts.append(o: .space) parts.append(contentsOf: rhs.parts) return SwifQLableParts(parts: parts) } infix operator **: AdditionPrecedence public func ** (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = lhs.parts parts.append(o: .space) parts.append(o: .custom("*")) parts.append(o: .space) parts.append(contentsOf: rhs.parts) return SwifQLableParts(parts: parts) } public func / (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = lhs.parts parts.append(o: .space) parts.append(o: .custom("/")) parts.append(o: .space) parts.append(contentsOf: rhs.parts) return SwifQLableParts(parts: parts) } //% prefix for LIKE prefix operator % prefix public func %(lhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(o: .custom("%")) parts.append(contentsOf: lhs.parts) return SwifQLableParts(parts: parts) } //% postfix for LIKE postfix operator % postfix public func %(rhs: SwifQLable) -> SwifQLable { var parts = rhs.parts parts.append(o: .custom("%")) return SwifQLableParts(parts: parts) } //1 opening bracket prefix operator | prefix public func |(lhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(o: .openBracket) parts.append(contentsOf: lhs.parts) return SwifQLableParts(parts: parts) } //2 opening brackets prefix operator || prefix public func ||(lhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(contentsOf: lhs.parts) return SwifQLableParts(parts: parts) } //3 opening brackets prefix operator ||| prefix public func |||(lhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(contentsOf: lhs.parts) return SwifQLableParts(parts: parts) } //4 opening brackets prefix operator |||| prefix public func ||||(lhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(contentsOf: lhs.parts) return SwifQLableParts(parts: parts) } //5 opening brackets prefix operator ||||| prefix public func |||||(lhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(contentsOf: lhs.parts) return SwifQLableParts(parts: parts) } //6 opening brackets prefix operator |||||| prefix public func ||||||(lhs: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(o: .openBracket) parts.append(contentsOf: lhs.parts) return SwifQLableParts(parts: parts) } //1 closing bracket postfix operator | postfix public func |(rhs: SwifQLable) -> SwifQLable { var parts = rhs.parts parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } //2 closing brackets postfix operator || postfix public func ||(rhs: SwifQLable) -> SwifQLable { var parts = rhs.parts parts.append(o: .closeBracket) parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } //3 closing brackets postfix operator ||| postfix public func |||(rhs: SwifQLable) -> SwifQLable { var parts = rhs.parts parts.append(o: .closeBracket) parts.append(o: .closeBracket) parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } //4 closing brackets postfix operator |||| postfix public func ||||(rhs: SwifQLable) -> SwifQLable { var parts = rhs.parts parts.append(o: .closeBracket) parts.append(o: .closeBracket) parts.append(o: .closeBracket) parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } //5 closing brackets postfix operator ||||| postfix public func |||||(rhs: SwifQLable) -> SwifQLable { var parts = rhs.parts parts.append(o: .closeBracket) parts.append(o: .closeBracket) parts.append(o: .closeBracket) parts.append(o: .closeBracket) parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } //6 closing brackets postfix operator |||||| postfix public func ||||||(rhs: SwifQLable) -> SwifQLable { var parts = rhs.parts parts.append(o: .closeBracket) parts.append(o: .closeBracket) parts.append(o: .closeBracket) parts.append(o: .closeBracket) parts.append(o: .closeBracket) parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } postfix operator * postfix public func *(lhs: SwifQLable) -> SwifQLable { var parts = lhs.parts parts.appendSpaceIfNeeded() parts.append(o: .custom("*")) return SwifQLableParts(parts: parts) } postfix operator .* postfix public func .*(lhs: SwifQLable) -> SwifQLable { var parts = lhs.parts parts.append(o: .custom(".*")) parts.append(o: .space) return SwifQLableParts(parts: parts) } ================================================ FILE: Sources/SwifQL/Keypathable.swift ================================================ // // Keypathable.swift // SwifQL // // Created by Mihael Isaev on 11/11/2018. // import Foundation public protocol Keypathable: KeyPathLastPath { var schema: String? { get } var table: String { get } var paths: [String] { get } var shortPath: String { get } var lastPath: String { get } } extension KeyPath: KeyPathLastPath where Root: Table, Value: ColumnRepresentable { public var lastPath: String { Root.key(for: self) } } extension KeyPath: Keypathable where Root: Table, Value: ColumnRepresentable { public var schema: String? { (Root.self as? Schemable.Type)?.schemaName } public var table: String { Root.tableName } public var paths: [String] { [Root.key(for: self)] } public var shortPath: String { Root.key(for: self) } } ================================================ FILE: Sources/SwifQL/Operators.swift ================================================ // // Operator.swift // SwifQL // // Created by Mihael Isaev on 02/08/2019. // import Foundation public typealias Op = Operator public typealias Operator = SwifQLPartOperator extension SwifQLPartOperator { public typealias Result = SwifQLPartOperator public static var left: Result { "LEFT".operator } public static var right: Result { "RIGHT".operator } public static var inner: Result { "INNER".operator } public static var outer: Result { "OUTER".operator } public static var cross: Result { "CROSS".operator } public static var lateral: Result { "LATERAL".operator } public static var action: Result { "ACTION".operator } public static var no: Result { "NO".operator } public static var references: Result { "REFERENCES".operator } public static var check: Result { "CHECK".operator } public static var add: Result { "ADD".operator } public static var primary: Result { "PRIMARY".operator } public static var key: Result { "KEY".operator } public static var unique: Result { "UNIQUE".operator } public static var select: Result { "SELECT".operator } public static var distinct: Result { "DISTINCT".operator } public static var `as`: Result { "as".operator } public static var any: Result { "ANY".operator } public static var delete: Result { "DELETE".operator } public static var from: Result { "FROM".operator } public static var join: Result { "JOIN".operator } public static var `where`: Result { "WHERE".operator } public static var having: Result { "HAVING".operator } public static var group: Result { "GROUP".operator } public static var order: Result { "ORDER".operator } public static var by: Result { "BY".operator } public static var insert: Result { "INSERT".operator } public static var into: Result { "INTO".operator } public static var values: Result { "VALUES".operator } public static var union: Result { "UNION".operator } public static var all: Result { "ALL".operator } public static var returning: Result { "RETURNING".operator } public static var exists: Result { "EXISTS".operator } public static var and: Result { "AND".operator } public static var or: Result { "OR".operator } public static var greaterThan: Result { ">".operator } public static var lessThan: Result { "<".operator } public static var greaterThanOrEqual: Result { ">=".operator } public static var lessThanOrEqual: Result { "<=".operator } public static var equal: Result { "=".operator } public static var notEqual: Result { "!=".operator } public static var `if`: Result { "IF".operator } public static var `in`: Result { "IN".operator } public static var notIn: Result { "NOT IN".operator } public static var like: Result { "LIKE".operator } public static var notLike: Result { "NOT LIKE".operator } public static var ilike: Result { "ILIKE".operator } public static var notILike: Result { "NOT ILIKE".operator } public static var fulltext: Result { "@@".operator } public static var isNull: Result { "IS NULL".operator } public static var isNotNull: Result { "IS NOT NULL".operator } public static var contains: Result { "@>".operator } public static var containedBy: Result { "<@".operator } public static var on: Result { "ON".operator } public static var `case`: Result { "CASE".operator } public static var when: Result { "WHEN".operator } public static var then: Result { "THEN".operator } public static var `else`: Result { "ELSE".operator } public static var end: Result { "END".operator } public static var null: Result { "NULL".operator } public static var `do`: Result { "DO".operator } public static var conflict: Result { "CONFLICT".operator } public static var constraint: Result { "CONSTRAINT".operator } public static var nothing: Result { "NOTHING".operator } public static var asc: Result { "ASC".operator } public static var desc: Result { "DESC".operator } public static var limit: Result { "LIMIT".operator } public static var offset: Result { "OFFSET".operator } public static var `for`: Result { "FOR".operator } public static var filter: Result { "FILTER".operator } public static var array: Result { "ARRAY".operator } public static var doubleDollar: Result { "$$".operator } public static var between: Result { "BETWEEN".operator } public static var notBetween: Result { "NOT BETWEEN".operator } public static var not: Result { "NOT".operator } public static var timestamp: Result { "TIMESTAMP".operator } public static var with: Result { "WITH".operator } public static var timeZone: Result { "TIME ZONE".operator } public static var epoch: Result { "EPOCH".operator } public static var interval: Result { "INTERVAL".operator } public static var date: Result { "DATE".operator } public static var millenium: Result { "MILLENNIUM".operator } public static var microseconds: Result { "MICROSECONDS".operator } public static var milliseconds: Result { "MILLISECONDS".operator } public static var isoYear: Result { "ISOYEAR".operator } public static var isoDoW: Result { "ISODOW".operator } public static var hour: Result { "HOUR".operator } public static var time: Result { "TIME".operator } public static var minute: Result { "MINUTE".operator } public static var month: Result { "MONTH".operator } public static var quarter: Result { "QUARTER".operator } public static var second: Result { "SECOND".operator } public static var week: Result { "WEEK".operator } public static var year: Result { "YEAR".operator } public static var decade: Result { "DECADE".operator } public static var century: Result { "CENTURY".operator } public static var overlaps: Result { "OVERLAPS".operator } public static var over: Result { "OVER".operator } public static var doublePrecision: Result { "DOUBLE PRECISION".operator } public static var nulls: Result { "NULLS".operator } public static var first: Result { "FIRST".operator } public static var last: Result { "LAST".operator } public static var create: Result { "CREATE".operator } public static var index: Result { "INDEX".operator } public static var type: Result { "TYPE".operator } public static var function: Result { "FUNCTION".operator } public static var table: Result { "TABLE".operator } public static var `enum`: Result { "ENUM".operator } public static var range: Result { "RANGE".operator } public static var subtype: Result { "SUBTYPE".operator } public static var subtypeOpClass: Result { "SUBTYPE_OPCLASS".operator } public static var collate: Result { "COLLATE".operator } public static var collation: Result { "COLLATION".operator } public static var collatable: Result { "COLLATABLE".operator } public static var canonical: Result { "CANONICAL".operator } public static var subtypeDiff: Result { "SUBTYPE_DIFF".operator } public static var input: Result { "INPUT".operator } public static var output: Result { "OUTPUT".operator } public static var receive: Result { "RECEIVE".operator } public static var send: Result { "SEND".operator } public static var typmodIn: Result { "TYPMOD_IN".operator } public static var typmodOut: Result { "TYPMOD_OUT".operator } public static var analyze: Result { "ANALYZE".operator } public static var internalLength: Result { "INTERNALLENGTH".operator } public static var variable: Result { "VARIABLE".operator } public static var passedByValue: Result { "PASSEDBYVALUE".operator } public static var alignment: Result { "ALIGNMENT".operator } public static var storage: Result { "STORAGE".operator } public static var category: Result { "CATEGORY".operator } public static var preferred: Result { "PREFERRED".operator } public static var `default`: Result { "DEFAULT".operator } public static var element: Result { "ELEMENT".operator } public static var delimiter: Result { "DELIMITER".operator } public static var returns: Result { "RETURNS".operator } public static var setOf: Result { "SETOF".operator } public static var begin: Result { "BEGIN".operator } public static var commit: Result { "COMMIT".operator } public static var rollback: Result { "ROLLBACK".operator } public static var `return`: Result { "RETURN".operator } public static var raise: Result { "RAISE".operator } public static var exception: Result { "EXCEPTION".operator } public static var replace: Result { "REPLACE".operator } public static var semicolon: Result { ";".operator } public static var openBracket: Result { "(".operator } public static var closeBracket: Result { ")".operator } public static var openSquareBracket: Result { "[".operator } public static var closeSquareBracket: Result { "]".operator } public static var openBrace: Result { "{".operator } public static var closeBrace: Result { "}".operator } public static var comma: Result { ",".operator } public static var period: Result { ".".operator } public static var space: Result { `_` } public static var `_`: Result { " ".operator } public static var using: Result { "USING".operator } public static var owner: Result { "OWNER".operator } public static var to: Result { "TO".operator } public static var currentUser: Result { "CURRENT_USER".operator } public static var sessionUser: Result { "SESSION_USER".operator } public static var rename: Result { "RENAME".operator } public static var column: Result { "COLUMN".operator } public static var attribute: Result { "ATTRIBUTE".operator } public static var cascade: Result { "CASCADE".operator } public static var restrict: Result { "RESTRICT".operator } public static var schema: Result { "SCHEMA".operator } public static var foreign: Result { "FOREIGN".operator } public static var value: Result { "VALUE".operator } public static var before: Result { "BEFORE".operator } public static var after: Result { "AFTER".operator } public static var drop: Result { "DROP".operator } public static var update: Result { "UPDATE".operator } public static var alter: Result { "ALTER".operator } public static var set: Result { "SET".operator } public static var data: Result { "DATA".operator } public static var partition: Result { "PARTITION".operator } public static var window: Result { "WINDOW".operator } public static func custom(_ v: String) -> Result { v.operator } public var left: Result { concatWith(.left) } public var right: Result { concatWith(.right) } public var inner: Result { concatWith(.inner) } public var outer: Result { concatWith(.outer) } public var cross: Result { concatWith(.cross) } public var lateral: Result { concatWith(.lateral) } public var no: Result { concatWith(.no) } public var action: Result { concatWith(.action) } public var references: Result { concatWith(.references) } public var add: Result { concatWith(.add) } public var check: Result { concatWith(.check) } public var primary: Result { concatWith(.primary) } public var key: Result { concatWith(.key) } public var unique: Result { concatWith(.unique) } public var select: Result { concatWith(.select) } public var distinct: Result { concatWith(.distinct) } public var `as`: Result { concatWith(.as) } public var any: Result { concatWith(.any) } public var delete: Result { concatWith(.delete) } public var from: Result { concatWith(.from) } public var join: Result { concatWith(.join) } public var `where`: Result { concatWith(.where) } public var having: Result { concatWith(.having) } public var group: Result { concatWith(.group) } public var order: Result { concatWith(.order) } public var by: Result { concatWith(.by) } public var insert: Result { concatWith(.insert) } public var into: Result { concatWith(.into) } public var values: Result { concatWith(.values) } public var union: Result { concatWith(.union) } public var returning: Result { concatWith(.returning) } public var exists: Result { concatWith(.exists) } public var and: Result { concatWith(.and) } public var or: Result { concatWith(.or) } public var greaterThan: Result { concatWith(.greaterThan) } public var lessThan: Result { concatWith(.lessThan) } public var greaterThanOrEqual: Result { concatWith(.greaterThanOrEqual) } public var lessThanOrEqual: Result { concatWith(.lessThanOrEqual) } public var equal: Result { concatWith(.equal) } public var notEqual: Result { concatWith(.notEqual) } public var `if`: Result { concatWith(.if) } public var `in`: Result { concatWith(.in) } public var notIn: Result { concatWith(.notIn) } public var like: Result { concatWith(.like) } public var notLike: Result { concatWith(.notLike) } public var ilike: Result { concatWith(.ilike) } public var notILike: Result { concatWith(.notILike) } public var fulltext: Result { concatWith(.fulltext) } public var isNull: Result { concatWith(.isNull) } public var isNotNull: Result { concatWith(.isNotNull) } public var contains: Result { concatWith(.contains) } public var containedBy: Result { concatWith(.containedBy) } public var on: Result { concatWith(.on) } public var `case`: Result { concatWith(.case) } public var when: Result { concatWith(.when) } public var then: Result { concatWith(.then) } public var `else`: Result { concatWith(.else) } public var end: Result { concatWith(.end) } public var null: Result { concatWith(.null) } public var `do`: Result { concatWith(.do) } public var conflict: Result { concatWith(.conflict) } public var constraint: Result { concatWith(.constraint) } public var nothing: Result { concatWith(.nothing) } public var asc: Result { concatWith(.asc) } public var desc: Result { concatWith(.desc) } public var limit: Result { concatWith(.limit) } public var offset: Result { concatWith(.offset) } public var `for`: Result { concatWith(.for) } public var filter: Result { concatWith(.filter) } public var array: Result { concatWith(.array) } public var doubleDollar: Result { concatWith(.doubleDollar) } public var between: Result { concatWith(.between) } public var notBetween: Result { concatWith(.notBetween) } public var not: Result { concatWith(.not) } public var timestamp: Result { concatWith(.timestamp) } public var with: Result { concatWith(.with) } public var timeZone: Result { concatWith(.timeZone) } public var epoch: Result { concatWith(.epoch) } public var interval: Result { concatWith(.interval) } public var date: Result { concatWith(.date) } public var millenium: Result { concatWith(.millenium) } public var microseconds: Result { concatWith(.microseconds) } public var milliseconds: Result { concatWith(.milliseconds) } public var isoYear: Result { concatWith(.isoYear) } public var isoDoW: Result { concatWith(.isoDoW) } public var hour: Result { concatWith(.hour) } public var time: Result { concatWith(.time) } public var minute: Result { concatWith(.minute) } public var month: Result { concatWith(.month) } public var quarter: Result { concatWith(.quarter) } public var second: Result { concatWith(.second) } public var week: Result { concatWith(.week) } public var year: Result { concatWith(.year) } public var decade: Result { concatWith(.decade) } public var century: Result { concatWith(.century) } public var overlaps: Result { concatWith(.overlaps) } public var over: Result { concatWith(.over) } public var doublePrecision: Result { concatWith(.doublePrecision) } public var nulls: Result { concatWith(.nulls) } public var first: Result { concatWith(.first) } public var last: Result { concatWith(.last) } public var create: Result { concatWith(.create) } public var index: Result { concatWith(.index) } public var type: Result { concatWith(.type) } public var function: Result { concatWith(.function) } public var table: Result { concatWith(.table) } public var `enum`: Result { concatWith(.enum) } public var range: Result { concatWith(.range) } public var subtype: Result { concatWith(.subtype) } public var subtypeOpClass: Result { concatWith(.subtypeOpClass) } public var collate: Result { concatWith(.collate) } public var collation: Result { concatWith(.collation) } public var collatable: Result { concatWith(.collatable) } public var canonical: Result { concatWith(.canonical) } public var subtypeDiff: Result { concatWith(.subtypeDiff) } public var input: Result { concatWith(.input) } public var output: Result { concatWith(.output) } public var receive: Result { concatWith(.receive) } public var send: Result { concatWith(.send) } public var typmodIn: Result { concatWith(.typmodIn) } public var typmodOut: Result { concatWith(.typmodOut) } public var analyze: Result { concatWith(.analyze) } public var internalLength: Result { concatWith(.internalLength) } public var variable: Result { concatWith(.variable) } public var passedByValue: Result { concatWith(.passedByValue) } public var alignment: Result { concatWith(.alignment) } public var storage: Result { concatWith(.storage) } public var category: Result { concatWith(.category) } public var preferred: Result { concatWith(.preferred) } public var `default`: Result { concatWith(.default) } public var element: Result { concatWith(.element) } public var delimiter: Result { concatWith(.delimiter) } public var returns: Result { concatWith(.returns) } public var setOf: Result { concatWith(.setOf) } public var begin: Result { concatWith(.begin) } public var commit: Result { concatWith(.commit) } public var rollback: Result { concatWith(.rollback) } public var `return`: Result { concatWith(.return) } public var raise: Result { concatWith(.raise) } public var exception: Result { concatWith(.exception) } public var replace: Result { concatWith(.replace) } public var semicolon: Result { concatWith(.semicolon) } public var openBracket: Result { concatWith(.openBracket) } public var closeBracket: Result { concatWith(.closeBracket) } public var openSquareBracket: Result { concatWith(.openSquareBracket) } public var closeSquareBracket: Result { concatWith(.closeSquareBracket) } public var openBrace: Result { concatWith(.openBrace) } public var closeBrace: Result { concatWith(.closeBrace) } public var comma: Result { concatWith(.comma) } public var period: Result { concatWith(.period) } public var space: Result { concatWith(.space) } public var `_`: Result { concatWith(.`_`) } public var using: Result { concatWith(.using) } public var owner: Result { concatWith(.owner) } public var to: Result { concatWith(.to) } public var currentUser: Result { concatWith(.currentUser) } public var sessionUser: Result { concatWith(.sessionUser) } public var rename: Result { concatWith(.rename) } public var column: Result { concatWith(.column) } public var attribute: Result { concatWith(.attribute) } public var cascade: Result { concatWith(.cascade) } public var restrict: Result { concatWith(.restrict) } public var schema: Result { concatWith(.schema) } public var foreign: Result { concatWith(.foreign) } public var value: Result { concatWith(.value) } public var before: Result { concatWith(.before) } public var after: Result { concatWith(.after) } public var drop: Result { concatWith(.drop) } public var update: Result { concatWith(.update) } public var alter: Result { concatWith(.alter) } public var set: Result { concatWith(.set) } public var data: Result { concatWith(.data) } public var partition: Result { concatWith(.partition) } public var window: Result { concatWith(.window) } public func custom(_ v: String) -> Result { concatWith(.custom(v)) } private func concatWith(_ operator: Result) -> Result { (_value + `operator`._value).operator } } extension String { fileprivate var `operator`: SwifQLPartOperator { .init(self) } } ================================================ FILE: Sources/SwifQL/Parts/AliasPart.swift ================================================ // // AliasPart.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation public struct SwifQLPartAlias: SwifQLPart { var alias: String init (_ alias: String) { self.alias = alias } } ================================================ FILE: Sources/SwifQL/Parts/ArrayPart.swift ================================================ // // ArrayPart.swift // SwifQL // // Created by Mihael Isaev on 06.06.2020. // import Foundation public protocol SwifQLPartArray: SwifQLPart { var elements: [SwifQLable] { get } } ================================================ FILE: Sources/SwifQL/Parts/BoolPart.swift ================================================ // // BoolPart.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation public typealias SwifQLBool = SwifQLPartBool public struct SwifQLPartBool: SwifQLPart, SwifQLable { public var parts: [SwifQLPart] { [self] } let value: Bool public init (_ value: Bool) { self.value = value } } ================================================ FILE: Sources/SwifQL/Parts/ColumnPart.swift ================================================ // // ColumnPart.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation public struct SwifQLPartColumn: SwifQLPart { public var name: String public init (_ name: String) { self.name = name } } ================================================ FILE: Sources/SwifQL/Parts/DatePart.swift ================================================ // // DatePart.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation public struct SwifQLPartDate: SwifQLPart { public var date: Date public init (_ date: Date) { self.date = date } } ================================================ FILE: Sources/SwifQL/Parts/HybridOperatorPart.swift ================================================ // // HybridOperatorPart.swift // // // Created by TierraCero on 5/30/23. // import Foundation public struct SwifQLHybridOperator: SwifQLPart, Equatable { var _psql: SwifQLPartOperator var _mysql: SwifQLPartOperator public init (_ psql: SwifQLPartOperator, _ mysql: SwifQLPartOperator) { self._psql = psql self._mysql = mysql } } extension SwifQLHybridOperator: SwifQLable { public var parts: [SwifQLPart] { [self] } } ================================================ FILE: Sources/SwifQL/Parts/KeyPathPart.swift ================================================ // // KeyPathPart.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation public struct SwifQLPartKeyPath: SwifQLKeyPathable { public var schema: String? public var table: String? public var paths: [String] public var asText: Bool // FIXME: instead of `asText` here create some protocol for path which will support `asText` for each part of path public init (schema: String? = nil, table: String? = nil, paths: String..., asText: Bool = false) { self.init(schema: schema, table: table, paths: paths, asText: asText) } public init (schema: String? = nil, table: String? = nil, paths: [String], asText: Bool = false) { self.schema = schema self.table = table self.paths = paths self.asText = asText } public var column: SwifQLPartColumn { .init(paths[0]) } } extension SwifQLPartKeyPath: SwifQLable { public var parts: [SwifQLPart] { SwifQLableParts(parts: self).parts } } ================================================ FILE: Sources/SwifQL/Parts/NullPart.swift ================================================ // // NullPart.swift // SwifQL // // Created by Mihael Isaev on 31.10.2020. // import Foundation public var SwifQLNull: SwifQLPartNull { .init() } public struct SwifQLPartNull: SwifQLPart, SwifQLable { public var parts: [SwifQLPart] { [self] } public init () {} } ================================================ FILE: Sources/SwifQL/Parts/OperatorPart.swift ================================================ // // OperatorPart.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation public struct SwifQLPartOperator: SwifQLPart, Equatable { var _value: String public init (_ value: String) { self._value = value } } extension SwifQLPartOperator: SwifQLable { public var parts: [SwifQLPart] { [self] } } ================================================ FILE: Sources/SwifQL/Parts/SafeValuePart.swift ================================================ // // SafeValuePart.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation public struct SwifQLPartSafeValue: SwifQLPart { var safeValue: Any? public init (_ value: Any?) { safeValue = value } } ================================================ FILE: Sources/SwifQL/Parts/TablePart.swift ================================================ // // TablePart.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation public struct SwifQLPartSchema: SwifQLPart { public var schema: String? public init (_ schema: String?) { self.schema = schema } } public struct SwifQLPartTable: SwifQLPart { public var schema: String? public var table: String public init (_ table: String) { self.table = table } public init (schema: String?, table: String) { self.schema = schema self.table = table } } ================================================ FILE: Sources/SwifQL/Parts/TableWithAliasPart.swift ================================================ // // TableWithAliasPart.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation public struct SwifQLPartTableWithAlias: SwifQLPart { public var schema: String? public var table, alias: String public init (schema: String?, table: String, alias: String) { self.schema = schema self.table = table self.alias = alias } } ================================================ FILE: Sources/SwifQL/Parts/UnsafeValuePart.swift ================================================ // // UnsafeValuePart.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation public struct SwifQLPartUnsafeValue: SwifQLPart { var unsafeValue: Encodable public init (_ value: Encodable) { unsafeValue = value } } ================================================ FILE: Sources/SwifQL/Path/Path+Column.swift ================================================ // // Path+Column.swift // SwifQL // // Created by Mihael Isaev on 04.01.2020. // import Foundation extension Path { public struct Column { public let paths: [String] public init (_ paths: String...) { self.paths = paths } public init (_ paths: [String]) { self.paths = paths } } } extension Path.Column: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartKeyPath(table: nil, paths: paths)] } } extension Path.Column: KeyPathLastPath { public var lastPath: String { paths.last ?? "" } } ================================================ FILE: Sources/SwifQL/Path/Path+Schema.swift ================================================ // // Path+Schema.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // import Foundation extension Path { public struct Schema { public let name: String? public init (_ name: String?) { self.name = name } @discardableResult public func table(_ table: Table) -> SchemaWithTable { self.table(table.name) } @discardableResult public func table(_ table: String) -> SchemaWithTable { .init(schema: name, table: table) } } } extension Path.Schema: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartSchema(name)] } } extension Path.Schema: KeyPathLastPath { public var lastPath: String { "" } } ================================================ FILE: Sources/SwifQL/Path/Path+SchemaWithTable.swift ================================================ // // Path+SchemaWithTable.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // import Foundation extension Path { public struct SchemaWithTable { public let schema: String? public let table: String public init (schema: String?, table: String) { self.schema = schema self.table = table } @discardableResult public func column(_ column: Column) -> SchemaWithTableAndColumn { self.column(column.paths) } @discardableResult public func column(_ paths: String...) -> SchemaWithTableAndColumn { column(paths) } @discardableResult public func column(_ paths: [String]) -> SchemaWithTableAndColumn { SchemaWithTableAndColumn(schema: schema, table: table, paths: paths) } } } extension Path.SchemaWithTable: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartTable(schema: schema, table: table)] } } ================================================ FILE: Sources/SwifQL/Path/Path+SchemaWithTableAndColumn.swift ================================================ // // Path+SchemaWithTableAndColumn.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // import Foundation extension Path { public struct SchemaWithTableAndColumn { let schema: String? let table: String let paths: [String] } } extension Path.SchemaWithTableAndColumn: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartKeyPath(schema: schema, table: table, paths: paths)] } } extension Path.SchemaWithTableAndColumn: KeyPathLastPath { public var lastPath: String { paths.last ?? "" } } ================================================ FILE: Sources/SwifQL/Path/Path+Table.swift ================================================ // // Path+Table.swift // SwifQL // // Created by Mihael Isaev on 07.01.2020. // import Foundation extension Path { public struct Table { public let name: String public init (_ name: String) { self.name = name } @discardableResult public func column(_ column: Column) -> TableWithColumn { self.column(column.paths) } @discardableResult public func column(_ paths: String...) -> TableWithColumn { column(paths) } @discardableResult public func column(_ paths: [String]) -> TableWithColumn { TableWithColumn(table: name, paths: paths) } } } extension Path.Table: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartTable(name)] } } extension Path.Table: KeyPathLastPath { public var lastPath: String { "" } } ================================================ FILE: Sources/SwifQL/Path/Path+TableWithColumn.swift ================================================ // // TableWithColumn.swift // SwifQL // // Created by Mihael Isaev on 07.01.2020. // import Foundation extension Path { public struct TableWithColumn { let table: String let paths: [String] } } extension Path.TableWithColumn: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartKeyPath(table: table, paths: paths)] } } extension Path.TableWithColumn: KeyPathLastPath { public var lastPath: String { paths.last ?? "" } } ================================================ FILE: Sources/SwifQL/Path/Path.swift ================================================ // // Path.swift // // // Created by Mihael Isaev on 26.01.2020. // import Foundation public struct Path {} ================================================ FILE: Sources/SwifQL/Predicates.swift ================================================ // // Predicates.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation public struct SwifQLPredicate: SwifQLable { public var parts: [SwifQLPart] public init (operator: SwifQLPartOperator, lhs: SwifQLable, rhs: SwifQLable?) { parts = lhs.parts parts.append(o: .space) if let rhs = rhs { parts.append(o: `operator`) parts.append(o: .space) parts.append(contentsOf: rhs.parts) } else { switch `operator` { case .equal: parts.append(o: .isNull) case .notEqual: parts.append(o: .isNotNull) default: parts.append(o: .null) } } } } public func > (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { SwifQLPredicate(operator: .greaterThan, lhs: lhs, rhs: rhs) } public func < (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { SwifQLPredicate(operator: .lessThan, lhs: lhs, rhs: rhs) } public func >= (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { SwifQLPredicate(operator: .greaterThanOrEqual, lhs: lhs, rhs: rhs) } public func <= (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { SwifQLPredicate(operator: .lessThanOrEqual, lhs: lhs, rhs: rhs) } public func == (lhs: T, rhs: T.AType) -> SwifQLable where T: SwifQLUniversalKeyPath, T: SwifQLable, T.AType: RawRepresentable, T.AType: Encodable { SwifQLPredicate(operator: .equal, lhs: lhs, rhs: SwifQLableParts(parts: SwifQLPartSafeValue(rhs.rawValue))) } public func == (lhs: T, rhs: T.AType.RawValue) -> SwifQLable where T: SwifQLUniversalKeyPath, T: SwifQLable, T.AType: RawRepresentable, T.AType.RawValue: SwifQLable { SwifQLPredicate(operator: .equal, lhs: lhs, rhs: SwifQLableParts(parts: SwifQLPartSafeValue(rhs))) } public func != (lhs: T, rhs: T.AType) -> SwifQLable where T: SwifQLUniversalKeyPath, T: SwifQLable, T.AType: RawRepresentable, T.AType: Encodable { SwifQLPredicate(operator: .notEqual, lhs: lhs, rhs: SwifQLableParts(parts: SwifQLPartSafeValue(rhs.rawValue))) } public func != (lhs: T, rhs: T.AType.RawValue) -> SwifQLable where T: SwifQLUniversalKeyPath, T: SwifQLable, T.AType: RawRepresentable, T.AType.RawValue: SwifQLable { SwifQLPredicate(operator: .notEqual, lhs: lhs, rhs: SwifQLableParts(parts: SwifQLPartSafeValue(rhs))) } public func == (lhs: SwifQLable, rhs: SwifQLable?) -> SwifQLable { SwifQLPredicate(operator: .equal, lhs: lhs, rhs: rhs) } public func != (lhs: SwifQLable, rhs: SwifQLable?) -> SwifQLable { SwifQLPredicate(operator: .notEqual, lhs: lhs, rhs: rhs) } public func == (lhs: SwifQLable, rhs: Bool) -> SwifQLable { SwifQLPredicate(operator: .equal, lhs: lhs, rhs: SwifQLPartBool(rhs)) } public func != (lhs: SwifQLable, rhs: Bool) -> SwifQLable { SwifQLPredicate(operator: .notEqual, lhs: lhs, rhs: SwifQLPartBool(rhs)) } public func == (lhs: Bool, rhs: SwifQLable) -> SwifQLable { SwifQLPredicate(operator: .equal, lhs: SwifQLPartBool(lhs), rhs: rhs) } public func != (lhs: Bool, rhs: SwifQLable) -> SwifQLable { SwifQLPredicate(operator: .notEqual, lhs: SwifQLPartBool(lhs), rhs: rhs) } public func && (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { SwifQLPredicate(operator: .and, lhs: lhs, rhs: rhs) } public func || (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { SwifQLPredicate(operator: .or, lhs: lhs, rhs: rhs) } /// Originally: @> infix operator ||> : AdditionPrecedence public func ||> (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { SwifQLPredicate(operator: .contains, lhs: lhs, rhs: rhs) } /// Originally: <@ infix operator <|| : AdditionPrecedence public func <|| (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { SwifQLPredicate(operator: .containedBy, lhs: lhs, rhs: rhs) } /// Originally: BETWEEN infix operator <> : AdditionPrecedence public func <> (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { SwifQLPredicate(operator: .between, lhs: lhs, rhs: rhs) } // TBD: Table 9.43. json and jsonb Operators (https://www.postgresql.org/docs/current/functions-json.html) // TBD: Table 9.44. Additional jsonb Operators (https://www.postgresql.org/docs/current/functions-json.html) //OR || ================================================ FILE: Sources/SwifQL/Prepared.swift ================================================ // // Prepared.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation public struct SwifQLPrepared { var _dialect: SQLDialect var _query: String var _values: [Encodable] var _formattedValues: [String] init (dialect: SQLDialect, query: String, values: [Encodable], formattedValues: [String]) { _dialect = dialect _query = query _values = values _formattedValues = formattedValues } public var plain: String { guard _values.count > 0 else { return _query } let formatter = SwifQLFormatter(_dialect, mode: .plain) return formatter.string(from: _query, with: _formattedValues) } public var splitted: SwifQLSplittedQuery { guard _values.count > 0 else { return .init(query: _query, values: _values) } let formatter = SwifQLFormatter(_dialect, mode: .binded) let result = formatter.string(from: _query, with: _formattedValues) return .init(query: result, values: _values) } } ================================================ FILE: Sources/SwifQL/QueryBuilderable.swift ================================================ // // QueryBuilderable.swift // SwifQL // // Created by Mihael Isaev on 18.05.2020. // import Foundation public protocol QueryBuilderable: AnyObject { var queryParts: QueryParts { get set } } extension QueryBuilderable { // MARK: Join @discardableResult public func join(_ mode: JoinMode, _ table: SwifQLable, on predicates: SwifQLable) -> Self { join(SwifQLJoinBuilder(mode, table, on: predicates)) } @discardableResult public func join(_ item: SwifQLJoinBuilder...) -> Self { join(item) } @discardableResult public func join(_ items: [SwifQLJoinBuilder]) -> Self { queryParts.joins.append(contentsOf: items) return self } // MARK: Where @discardableResult public func `where`(_ item: SwifQLable...) -> Self { `where`(item) } @discardableResult public func `where`(_ items: [SwifQLable]) -> Self { queryParts.wheres.append(contentsOf: items) return self } // MARK: Group by @discardableResult public func groupBy(_ item: SwifQLable...) -> Self { groupBy(item) } @discardableResult public func groupBy(_ items: [SwifQLable]) -> Self { queryParts.groupBy.append(contentsOf: items) return self } // MARK: Having @discardableResult public func having(_ item: SwifQLable...) -> Self { having(item) } @discardableResult public func having(_ items: [SwifQLable]) -> Self { queryParts.havings.append(contentsOf: items) return self } // MARK: Order by @discardableResult public func orderBy(_ item: OrderByItem...) -> Self { orderBy(item) } @discardableResult public func orderBy(_ items: [OrderByItem]) -> Self { queryParts.orderBy.append(contentsOf: items) return self } // MARK: Offset @discardableResult public func offset(_ value: Int) -> Self { queryParts.offset = value return self } // MARK: Limit @discardableResult public func limit(_ value: Int) -> Self { queryParts.limit = value return self } @discardableResult public func limit(_ offset: Int,_ limit: Int) -> Self { queryParts.limit = limit queryParts.offset = offset return self } } ================================================ FILE: Sources/SwifQL/QueryParts.swift ================================================ // // QueryParts.swift // SwifQL // // Created by Mihael Isaev on 18.05.2020. // import Foundation public class QueryParts { public var joins: [SwifQLJoinBuilder] = [] public var wheres: [SwifQLable] = [] public var groupBy: [SwifQLable] = [] public var havings: [SwifQLable] = [] public var orderBy: [OrderByItem] = [] public var offset: Int? public var limit: Int? public init () {} public func copy() -> QueryParts { let copy = QueryParts() copy.joins = joins copy.wheres = wheres copy.groupBy = groupBy copy.havings = havings copy.orderBy = orderBy copy.offset = offset copy.limit = limit return copy } public func buildQuery() -> SwifQLable { var query = SwifQL joins.forEach { query = query.join($0.mode, $0.table, on: $0.predicates) } wheres.enumerated().forEach { if $0.offset == 0 { query = query.where($0.element) } else { query = query && $0.element } } if groupBy.count > 0 { query = query.groupBy(groupBy) } havings.enumerated().forEach { if $0.offset == 0 { query = query.having($0.element) } else { query = query && $0.element } } if orderBy.count > 0 { query = query.orderBy(orderBy) } if let limit = limit { query = query.limit(limit) } if let offset = offset { query = query.offset(offset) } return query } public func appended(to query: SwifQLable) -> SwifQLable { let q = buildQuery() guard q.parts.count > 0 else { return query } return query.addQuery(q) } } ================================================ FILE: Sources/SwifQL/ReferentialAction.swift ================================================ // // ReferentialAction.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // import Foundation public struct ReferentialAction: SwifQLable { public var parts: [SwifQLPart] { query.parts } var query: SwifQLable public init (_ expression: SwifQLable) { query = expression } /// Produce an error indicating that the deletion or update /// would create a foreign key constraint violation. /// If the constraint is deferred, this error will be produced at constraint /// check time if there still exist any referencing rows. This is the default action. public static var noAction: ReferentialAction { .init(SwifQL.no.action) } /// Produce an error indicating that the deletion or update /// would create a foreign key constraint violation. /// This is the same as NO ACTION except that the check is not deferrable. public static var restrict: ReferentialAction { .init(SwifQL.restrict) } /// Delete any rows referencing the deleted row, /// or update the values of the referencing column(s) /// to the new values of the referenced columns, respectively. public static var cascade: ReferentialAction { .init(SwifQL.cascade) } /// Set the referencing column(s) to null. public static var setNull: ReferentialAction { .init(SwifQL.set.null) } /// Set the referencing column(s) to their default values. /// (There must be a row in the referenced table matching the default values, /// if they are not null, or the operation will fail.) public static var setDefault: ReferentialAction { .init(SwifQL.set.default) } } ================================================ FILE: Sources/SwifQL/ResultBuilders/SwifQLableResultBuilder.swift ================================================ //// //// SwifQLableResultBuilder.swift //// SwifQL //// //// Created by Mihael Isaev on 11.04.2020. //// // //import Foundation // //@resultBuilder public struct SwifQLableResultBuilder { // public typealias Block = () -> SwifQLable // // /// Builds an empty view from an block containing no statements, `{ }`. // public static func buildBlock() -> SwifQLable { [] } // // /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`) through unmodified. // public static func buildBlock(_ attrs: SwifQLable...) -> SwifQLable { // buildBlock(attrs) // } // // /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`) through unmodified. // public static func buildBlock(_ attrs: [SwifQLable]) -> SwifQLable { // // ViewBuilderItems(items: attrs.flatMap { $0.viewBuilderItems }) // } // // /// Provides support for "if" statements in multi-statement closures, producing an `Optional` view // /// that is visible only when the `if` condition evaluates `true`. // public static func buildIf(_ content: SwifQLable?) -> SwifQLable { // guard let content = content else { return [] } // return content // } // // /// Provides support for "if" statements in multi-statement closures, producing // /// ConditionalContent for the "then" branch. // public static func buildEither(first: SwifQLable) -> SwifQLable { // first // } // // /// Provides support for "if-else" statements in multi-statement closures, producing // /// ConditionalContent for the "else" branch. // public static func buildEither(second: SwifQLable) -> SwifQLable { // second // } //} // //public protocol SwifQLableResultBuilderItem { // var expressions: [SwifQLable] { get } //} // //extension SwifQLable: SwifQLableResultBuilderItem { // public var expressions: [SwifQLable] { [self] } //} //extension Array: SwifQLableResultBuilderItem where Element: SwifQLable { // public var expressions: [SwifQLable] { self } //} //extension Optional: SwifQLableResultBuilderItem where Wrapped: SwifQLable { // public var expressions: [SwifQLable] { // switch self { // case .none: return [] // case .some(let value): return [value] // } // } //} ================================================ FILE: Sources/SwifQL/Schema.swift ================================================ // // Schema.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // import Foundation @dynamicMemberLookup public struct Schema { let name: String public init (_ name: String) { self.name = name } public subscript(dynamicMember keyPath: KeyPath) -> SwifQLable { guard let k = keyPath as? Keypathable else { return "" } return SwifQLPartKeyPath(schema: name, table: k.table, paths: k.paths) } public var table: SwifQLable { Path.SchemaWithTable(schema: name, table: T.tableName) } public func column(_ paths: String...) -> Path.SchemaWithTableAndColumn { column(paths) } public func column(_ paths: [String]) -> Path.SchemaWithTableAndColumn { .init(schema: name, table: T.tableName, paths: paths) } public func `as`(_ alias: String) -> GenericTableAlias { .init(alias, schema: name) } } ================================================ FILE: Sources/SwifQL/Schemable.swift ================================================ // // Schemable.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // import Foundation public protocol Schemable { /// This schema's unique name. By default, this property is set to a `String` describing the type. static var schemaName: String { get } } // MARK: Optional extension Schemable { public static var schemaName: String { "public" } public static func table(_ table: Path.Table) -> Path.SchemaWithTable { Path.Schema(schemaName).table(table.name) } public static func table(_ table: String) -> Path.SchemaWithTable { Path.Schema(schemaName).table(table) } public static func path(_ table: String, _ paths: String...) -> Path.SchemaWithTableAndColumn { path(table, paths) } public static func path(_ table: String, _ paths: [String]) -> Path.SchemaWithTableAndColumn { Path.Schema(schemaName).table(table).column(paths) } } ================================================ FILE: Sources/SwifQL/SplittedQuery.swift ================================================ // // SplittedQuery.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation public struct SwifQLSplittedQuery { public var query: String public var values: [Encodable] init (query: String, values: [Encodable]) { self.query = query self.values = values } } ================================================ FILE: Sources/SwifQL/SwifQL.swift ================================================ // // SwifQL.swift // SwifQL // // Created by Mihael Isaev on 04/11/2018. // import Foundation public let SwifQL: SwifQLable = _SwifQL() public func SwifQL(_ query: SwifQLable) -> SwifQLable { _SwifQL(query) } private struct _SwifQL: SwifQLable { public var parts: [SwifQLPart] = [] public init (_ query: SwifQLable? = nil) { if let parts = query?.parts { self.parts = parts } } } infix operator ~ public func ~ (lhs: SwifQLable, rhs: SwifQLable) -> SwifQLable { var parts = lhs.parts parts.append(contentsOf: rhs.parts) return SwifQLableParts(parts: parts) } public func ~ (lhs: SwifQLable, rhs: SwifQLPartOperator) -> SwifQLable { var parts = lhs.parts parts.append(o: rhs) return SwifQLableParts(parts: parts) } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Action.swift ================================================ // // SwifQLable+Action.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // import Foundation //MARK: ACTION extension SwifQLable { public var action: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .action) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Add.swift ================================================ // // SwifQLable+Add.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation extension SwifQLable { public var add: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .add) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+AddQuery.swift ================================================ // // SwifQLable+AddQuery.swift // SwifQL // // Created by Mihael Isaev on 18.05.2020. // import Foundation extension SwifQLable { func addQuery(_ q: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(contentsOf: q.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+After.swift ================================================ // // SwifQLable+After.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation //MARK: AFTER extension SwifQLable { public var after: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .after) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+All.swift ================================================ // // SwifQLable+All.swift // App // // Created by Mihael Isaev on 30.09.2021. // import Foundation //MARK: ALL extension SwifQLable { public var all: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .all) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Alter.swift ================================================ // // SwifQLable+Alter.swift // SwifQL // // Created by Mihael Isaev on 26/11/2018. // import Foundation //MARK: ALTER extension SwifQLable { public var alter: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .alter) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+And.swift ================================================ // // SwifQLable+And.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: AND extension SwifQLable { public var and: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .and) return SwifQLableParts(parts: parts) } public func and(_ predicate: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .and) parts.append(o: .space) parts.append(contentsOf: predicate.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Any.swift ================================================ // // SwifQLable+Any.swift // // // Created by Mihael Isaev on 26.10.2020. // import Foundation //MARK: ANY extension SwifQLable { public var any: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .any) return SwifQLableParts(parts: parts) } public func any(_ subquery: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .any) parts.append(o: .openBracket) parts.append(contentsOf: subquery.parts) parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+As.swift ================================================ // // SwifQLable+As.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation //MARK: AS extension SwifQLable { public var `as`: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .as) return SwifQLableParts(parts: parts) } public func `as`(_ type: Type) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .as) parts.append(o: .space) parts.append(o: .custom(type.name)) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Asterisk.swift ================================================ // // SwifQLable+Asterisk.swift // SwifQL // // Created by Mihael Isaev on 31.01.2020. // import Foundation //MARK: * extension SwifQLable { public var asterisk: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .custom("*")) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Before.swift ================================================ // // SwifQLable+Before.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation //MARK: BEFORE extension SwifQLable { public var before: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .before) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Begin.swift ================================================ // // SwifQLable+Begin.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation //MARK: BEGIN extension SwifQLable { public var begin: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .begin) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Between.swift ================================================ // // SwifQLable+Between.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: BETWEEN extension SwifQLable { public func between(_ part: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .between) parts.append(o: .space) parts.append(contentsOf: part.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Cascade.swift ================================================ // // SwifQLable+Cascade.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // import Foundation //MARK: CASCADE extension SwifQLable { public var cascade: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .cascade) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Check.swift ================================================ // // SwifQLable+Check.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // import Foundation //MARK: CHECK extension SwifQLable { public var check: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .check) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Column.swift ================================================ // // SwifQLable+Rename.swift // SwifQL // // Created by Mihael Isaev on 17.08.2020. // import Foundation extension SwifQLable { public var column: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .column) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Commit.swift ================================================ // // SwifQLable+Commit.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation //MARK: COMMIT extension SwifQLable { public var commit: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .commit) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Conflict.swift ================================================ // // SwifQLable+Conflict.swift // SwifQL // // Created by Mihael Isaev on 24/07/2019. // import Foundation //MARK: Conflict extension SwifQLable { public var conflict: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .conflict) return SwifQLableParts(parts: parts) } public func conflict(_ paths: KeyPathLastPath...) -> SwifQLable { conflict(paths) } public func conflict(_ paths: [KeyPathLastPath]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .conflict) parts.append(o: .space) parts.append(o: .openBracket) for (i, p) in paths.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(SwifQLPartAlias(p.lastPath)) } parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Constraint.swift ================================================ // // SwifQLable+Constraint.swift // SwifQL // // Created by Mihael Isaev on 24/07/2019. // import Foundation //MARK: Constraint extension SwifQLable { public var constraint: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .constraint) return SwifQLableParts(parts: parts) } public func constraint(_ value: KeyPathLastPath) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .constraint) parts.append(o: .space) parts.append(SwifQLPartAlias(value.lastPath)) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Create.swift ================================================ // // SwifQLable+CreateType.swift // // // Created by Mihael Isaev on 23.01.2020. // import Foundation extension SwifQLable { public var create: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .create) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Default.swift ================================================ // // SwifQLable+Default.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // import Foundation //MARK: DEFAULT extension SwifQLable { public var `default`: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .default) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Delete.swift ================================================ // // SwifQLable+Delete.swift // SwifQL // // Created by Mihael Isaev on 26/11/2018. // import Foundation extension SwifQLable { public var delete: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .delete) return SwifQLableParts(parts: parts) } public func delete(from table: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .delete) parts.append(o: .space) parts.append(o: .from) parts.append(o: .space) parts.append(contentsOf: table.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Distinct.swift ================================================ // // SwifQLable+Distinct.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation //MARK: DISTINCT extension SwifQLable { public var distinct: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .distinct) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Do.swift ================================================ // // SwifQLable+Do.swift // SwifQL // // Created by Mihael Isaev on 24/07/2019. // import Foundation //MARK: Do extension SwifQLable { public var `do`: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .do) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Drop.swift ================================================ // // SwifQLable+Drop.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation extension SwifQLable { public var drop: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .drop) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+End.swift ================================================ // // SwifQLable+End.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation //MARK: END extension SwifQLable { public var end: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .end) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Epoch.swift ================================================ // // SwifQLable+Epoch.swift // SwifQL // // Created by Mihael Isaev on 02/08/2019. // import Foundation //MARK: Epoch extension SwifQLable { public var epoch: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .epoch) return SwifQLableParts(parts: parts) } public func epoch(with value: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .custom(Op.epoch._value.singleQuotted)) parts.append(o: .space) parts.append(o: .custom("+")) parts.append(o: .space) parts.append(contentsOf: value.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Exists.swift ================================================ // // SwifQLable+Exists.swift // SwifQL // // Created by Mihael Isaev on 23/07/2019. // import Foundation //MARK: Exists extension SwifQLable { public var exists: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .exists) return SwifQLableParts(parts: parts) } public func exists(_ predicates: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .exists) parts.append(o: .space) parts.append(o: .openBracket) parts.append(contentsOf: predicates.parts) parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Filter.swift ================================================ // // SwifQLable+Filter.swift // App // // Created by Mihael Isaev on 01/03/2019. // import Foundation //MARK: Filter extension SwifQLable { public func filter(where predicates: SwifQLable...) -> SwifQLable { filter(where: predicates) } public func filter(where predicates: [SwifQLable]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .filter) parts.append(o: .space) parts.append(o: .openBracket) parts.append(o: .where) parts.append(o: .space) predicates.enumerated().forEach { i, v in if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+From.swift ================================================ // // SwifQLable+From.swift // SwifQLCore // // Created by Mihael Isaev on 13/11/2018. // import Foundation //MARK: From extension SwifQLable { public func from(_ tables: SwifQLable...) -> SwifQLable { from(tables) } public func from(_ tables: [SwifQLable]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .from) parts.append(o: .space) for (i, v) in tables.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Fulltext.swift ================================================ // // SwifQLable+Fulltext.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: @@ extension SwifQLable { public func fulltext(_ part: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .fulltext) parts.append(o: .space) parts.append(contentsOf: part.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Function.swift ================================================ // // SwifQLable+Function.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation extension SwifQLable { public var `function`: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .function) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+GroupBy.swift ================================================ // // SwifQLable+GroupBy.swift // SwifQL // // Created by Mihael Isaev on 14/02/2019. // import Foundation //MARK: GROUP BY extension SwifQLable { public func groupBy(_ fields: SwifQLable...) -> SwifQLable { groupBy(fields) } public func groupBy(_ fields: [SwifQLable]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .group) parts.append(o: .space) parts.append(o: .by) parts.append(o: .space) for (i, v) in fields.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Having.swift ================================================ // // SwifQLable+Having.swift // SwifQL // // Created by Mihael Isaev on 14/02/2019. // import Foundation //MARK: Having extension SwifQLable { public func having(_ predicates: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .having) parts.append(o: .space) parts.append(contentsOf: predicates.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+ILike.swift ================================================ // // SwifQLable+iLike.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: ILIKE extension SwifQLable { /// Builds query with `ILIKE` parameter /// /// Example usage: /// ```swift /// let name = "John" /// SwifQL.select /// // ... /// .where((\User.$name).iLike(name)) /// ``` /// - Parameter part: `SwifQLable` element /// public func iLike(_ part: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .ilike) parts.append(o: .space) parts.append(contentsOf: part.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+If.swift ================================================ // // SwifQLable+If.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation //MARK: IF extension SwifQLable { public var `if`: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .if) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+In.swift ================================================ // // SwifQLable+In.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: IN extension SwifQLable { /// Builds query with `IN` parameter /// /// Example usage: /// ```swift /// SwifQL.select /// // ... /// .where((\User.$id).in(aUserID, bUserID)) /// ``` /// - Parameter items: comma separated list of `SwifQLable` elements /// public func `in`(_ items: SwifQLable...) -> SwifQLable { `in`(items) } /// Builds query with `IN` parameter /// /// Example usage: /// ```swift /// SwifQL.select /// // ... /// .where((\User.$id).in(userIDsArray)) /// ``` /// - Parameter items: Array of `[SwifQLable]` elements /// public func `in`(_ items: [SwifQLable]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .in) parts.append(o: .space) parts.append(o: .openBracket) if items.count == 1, let array = items.first as? AnySwifQLEnumArray { array.items.enumerated().forEach { i, v in if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(safe: v.anyRawValue) } } else { items.enumerated().forEach { i, v in if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } } parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+InsertInto.swift ================================================ // // SwifQLable+InsertInto.swift // SwifQLCore // // Created by Mihael Isaev on 13/11/2018. // import Foundation extension SwifQLable { public subscript (newColumns items: NewColumn...) -> SwifQLable { newColumns(items) } public subscript (newColumns items: [NewColumn]) -> SwifQLable { newColumns(items) } public func newColumns(_ items: NewColumn...) -> SwifQLable { newColumns(items) } public func newColumns(_ items: [NewColumn]) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .openBracket) items.enumerated().forEach { i, v in if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } public subscript (fields items: SwifQLable...) -> SwifQLable { fields(items) } public subscript (fields items: [SwifQLable]) -> SwifQLable { fields(items) } /// Represent just a list of fields in round brackets separated by comma public func fields(_ items: SwifQLable...) -> SwifQLable { fields(items) } /// Represent just a list of fields in round brackets separated by comma public func fields(_ items: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .openBracket) items.compactMap { v -> SwifQLPart? in if let part = v.parts.first as? SwifQLKeyPathable, let lastPath = part.paths.last { return SwifQLPartColumn(lastPath) } else if let name = v as? String { return SwifQLPartColumn(name) } return nil } .enumerated() .forEach { i, v in if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(v) } parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } public var insert: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .insert) return SwifQLableParts(parts: parts) } public var into: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .into) return SwifQLableParts(parts: parts) } public subscript (table item: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() if let name = item as? String { parts.append(SwifQLPartTable(name)) } else { parts.append(contentsOf: item.parts) } return SwifQLableParts(parts: parts) } public func insertInto(_ table: SwifQLable, fields: SwifQLable...) -> SwifQLable { insertInto(table, fields: fields) } public func insertInto(_ table: SwifQLable, fields: [SwifQLable]) -> SwifQLable { insert.into[table: table].fields(fields) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Interval.swift ================================================ // // SwifQLable+Interval.swift // SwifQL // // Created by Mihael Isaev on 02/08/2019. // import Foundation //MARK: Interval extension SwifQLable { public var interval: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .interval) return SwifQLableParts(parts: parts) } public func interval(_ expression: String) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .interval) parts.append(o: .space) parts.append(o: .custom(expression.singleQuotted)) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+IsNotNull.swift ================================================ // // SwifQLable+IsNotNull.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: IS NOT NULL extension SwifQLable { public var isNotNull: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .isNotNull) parts.append(o: .space) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+IsNull.swift ================================================ // // SwifQLable+IsNull.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: IS NULL extension SwifQLable { public var isNull: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .isNull) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Items.swift ================================================ // // SwifQLable+Items.swift // App // // Created by Mihael Isaev on 04.02.2020. // import Foundation extension SwifQLable { public subscript (items items: SwifQLable...) -> SwifQLable { self.items(items) } public subscript (items items: [SwifQLable]) -> SwifQLable { self.items(items) } /// Represent provided values in round brackets separated with comma public func items(_ items: SwifQLable...) -> SwifQLable { self.items(items) } /// Represent values provided as array public func items(_ items: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() for (i, v) in items.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } for p in v.parts { switch p { case let p as SwifQLPartKeyPath: parts.append(p.column) default: parts.append(p) } } } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Join.swift ================================================ // // SwifQLable+Join.swift // SwifQL // // Created by Mihael Isaev on 14/02/2019. // import Foundation //MARK: JOIN extension SwifQLable { /// Join tables /// /// Example usage: /// - join with subquery /// ```swift /// let u = TableAlias("u") /// let subquery = |SwifQL /// .select(Fn.count(\User.$id) => "users", /// \User.$groupID => "groupID") /// .from(User.table) /// .groupBy(\User.$groupID)| => u /// let query = SwifQL.select(..., u.users) /// .from(...) /// .join(.left, subquery, on: u.groupID == \Group.$id) /// .groupBy(..., u.users) /// ``` /// /// - Parameters: /// - mode: type of JOIN `JoinMode` /// - expression: `Table` or `subquery` /// - predicates: which columns should be used to make `JOIN` /// - Returns: `SwifQLable` public func join(_ mode: JoinMode = .none, _ expression: SwifQLable, on predicates: SwifQLable? = nil) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() let join = SwifQLJoinBuilder(mode, expression, on: predicates) parts.append(contentsOf: join.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Key.swift ================================================ // // SwifQLable+Key.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation //MARK: KEY extension SwifQLable { public var key: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .key) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Like.swift ================================================ // // SwifQLable+Like.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: LIKE extension SwifQLable { /// Builds query with `LIKE` parameter /// /// Example usage: /// ```swift /// let name = "John" /// SwifQL.select /// // ... /// .where((\User.$name).like(name)) /// ``` /// - Parameter part: `SwifQLable` element /// public func like(_ part: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .like) parts.append(o: .space) parts.append(contentsOf: part.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Limit.swift ================================================ // // SwifQLable+Limit.swift // SwifQL // // Created by Mihael Isaev on 14/02/2019. // import Foundation //MARK: LIMIT extension SwifQLable { public func limit(_ value: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .limit) parts.append(o: .space) parts.append(contentsOf: value.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+No.swift ================================================ // // SwifQLable+No.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // import Foundation //MARK: NO extension SwifQLable { public var no: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .no) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Not.swift ================================================ // // SwifQLable+Not.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: NOT extension SwifQLable { public var not: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .not) return SwifQLableParts(parts: parts) } public func not(_ part: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .not) parts.append(o: .space) parts.append(contentsOf: part.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+NotBetween.swift ================================================ // // SwifQLable+NotBetween.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: NOT BETWEEN extension SwifQLable { public func notBetween(_ part: SwifQLable) -> SwifQLable { SwifQLableParts(parts: self.parts).not.between(part) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+NotExists.swift ================================================ // // SwifQLable+NotExists.swift // SwifQL // // Created by Mihael Isaev on 23/07/2019. // import Foundation //MARK: Exists extension SwifQLable { public func notExists(_ predicates: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .not) parts.append(o: .space) parts.append(o: .exists) parts.append(o: .space) parts.append(o: .openBracket) parts.append(contentsOf: predicates.parts) parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+NotILike.swift ================================================ // // SwifQLable+NotILike.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation // MARK: NOT ILIKE extension SwifQLable { /// Builds query with `NOT ILIKE` parameter /// /// Example usage: /// ```swift /// let name = "John" /// SwifQL.select /// // ... /// .where((\User.$name).notILike(name)) /// ``` /// - Parameter part: `SwifQLable` element /// public func notILike(_ part: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .notILike) parts.append(o: .space) parts.append(contentsOf: part.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+NotIn.swift ================================================ // // SwifQLable+NotIn.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation // MARK: IN extension SwifQLable { /// Builds query with `NOT IN` parameter /// /// Example usage: /// ```swift /// SwifQL.select /// // ... /// .where((\User.$id).notIn(aUserID, bUserID)) /// ``` /// - Parameter items: comma separated list of `SwifQLable` elements /// public func notIn(_ items: SwifQLable...) -> SwifQLable { notIn(items) } /// Builds query with `NOT IN` parameter /// /// Example usage: /// ```swift /// SwifQL.select /// // ... /// .where((\User.$id).notIn(userIDsArray)) /// ``` /// - Parameter items: Array of `[SwifQLable]` elements /// public func notIn(_ items: [SwifQLable]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .notIn) parts.append(o: .space) parts.append(o: .openBracket) for (i, v) in items.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+NotLike.swift ================================================ // // SwifQLable+NotLike.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation // MARK: NOT LIKE extension SwifQLable { /// Builds query with `NOT LIKE` parameter /// /// Example usage: /// ```swift /// let name = "John" /// SwifQL.select /// // ... /// .where((\User.$name).notLike(name)) /// ``` /// - Parameter part: `SwifQLable` element /// public func notLike(_ part: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .notLike) parts.append(o: .space) parts.append(contentsOf: part.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Nothing.swift ================================================ // // SwifQLable+Nothing.swift // SwifQL // // Created by Mihael Isaev on 24/07/2019. // import Foundation //MARK: Nothing extension SwifQLable { public var nothing: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .nothing) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Null.swift ================================================ // // SwifQLable+Null.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation //MARK: NULL extension SwifQLable { /// use `null` property to compare column value with `SQL NULL` (aka Swift nil) /// /// Usage: /// ```swift /// SwifQL.select /// // ... /// .where(\User.$name == username /// && |\User.$status == "active" || \User.$updatedAt == SwifQL.null|) /// ``` public var null: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .null) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Offset.swift ================================================ // // SwifQLable+Offset.swift // SwifQL // // Created by Mihael Isaev on 14/02/2019. // import Foundation //MARK: OFFSET extension SwifQLable { public func offset(_ value: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .offset) parts.append(o: .space) parts.append(contentsOf: value.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+On.swift ================================================ // // SwifQLable+On.swift // SwifQL // // Created by Mihael Isaev on 24/07/2019. // import Foundation //MARK: On extension SwifQLable { public var on: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .on) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Or.swift ================================================ // // SwifQLable+Or.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: OR extension SwifQLable { public func or(_ predicate: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .or) parts.append(o: .space) parts.append(contentsOf: predicate.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+OrderBy.swift ================================================ // // SwifQLable+OrderBy.swift // SwifQL // // Created by Mihael Isaev on 14/02/2019. // import Foundation public struct OrderByItem: SwifQLable { // MARK: - Elements let elements: [SwifQLable] // MARK: - Direction public enum Direction { case asc, desc var `operator`: SwifQLPartOperator { switch self { case .asc: return .asc case .desc: return .desc } } } let direction: Direction // MARK: - Nulls public enum Nulls { case first, last var `operator`: SwifQLPartOperator { switch self { case .first: return .first case .last: return .last } } } let nulls: Nulls? // MARK: - Public static initializers // MARK: Direction /// Convenient method for situations if ascending flag is known only during runtime public static func direction(_ value: Direction, _ elements: SwifQLable..., nulls: Nulls? = nil) -> OrderByItem { direction(value, elements, nulls: nulls) } /// Convenient method for situations if ascending flag is known only during runtime public static func direction(_ value: Direction, _ elements: [SwifQLable], nulls: Nulls? = nil) -> OrderByItem { OrderByItem(elements: elements, direction: value, nulls: nulls) } // MARK: Ascending public static func asc(_ elements: SwifQLable...) -> OrderByItem { asc(elements, nulls: nil) } public static func asc(_ elements: [SwifQLable]) -> OrderByItem { asc(elements, nulls: nil) } public static func asc(_ elements: SwifQLable..., nulls: Nulls?) -> OrderByItem { asc(elements, nulls: nulls) } public static func asc(_ elements: [SwifQLable], nulls: Nulls?) -> OrderByItem { OrderByItem(elements: elements, direction: .asc, nulls: nulls) } // MARK: Descending public static func desc(_ elements: SwifQLable...) -> OrderByItem { desc(elements, nulls: nil) } public static func desc(_ elements: [SwifQLable]) -> OrderByItem { desc(elements, nulls: nil) } public static func desc(_ elements: SwifQLable..., nulls: Nulls?) -> OrderByItem { desc(elements, nulls: nulls) } public static func desc(_ elements: [SwifQLable], nulls: Nulls?) -> OrderByItem { OrderByItem(elements: elements, direction: .desc, nulls: nulls) } // MARK: Random /// Returns results in a random order. Hybrid operator that provides proper sintaxis acording to used language. public static var random: SwifQLHybridOperator { .random } // MARK: - SwifQLable public var parts: [SwifQLPart] { var parts: [SwifQLPart] = [] for (i, v) in elements.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } parts.append(o: .space) parts.append(o: direction.operator) if let nulls = nulls { parts.append(o: .space) parts.append(o: .nulls) parts.append(o: .space) parts.append(o: nulls.operator) } return parts } } //MARK: ORDER BY extension SwifQLable { /// Order query results by some rows ascending or descending /// # Simple example /// ```swift /// .orderBy(.asc(\User.email), .desc(\User.firstName)) /// ``` /// # Raw SQL (in PostgreSQL syntax) /// ```sql /// ORDER BY "User"."email" ASC, "User"."firstName" DESC /// ``` /// # Raw SQL (in MySQL syntax) /// ```sql /// ORDER BY "User"."email" ASC, "User"."firstName" DESC /// ``` /// /// # PostgreSQL nulls example /// ```swift /// .orderBy(.asc(\User.email, nulls: .first), .desc(\User.firstName, nulls: .last)) /// ``` /// # Raw SQL /// ```sql /// ORDER BY "User"."email" ASC NULLS FIRST, "User"."firstName" DESC NULLS LAST /// ``` /// /// # MySQL nulls example /// ```swift /// .orderBy(.asc(\User.email == nil, \User.email), .desc(\User.firstName != nil, \User.firstName)) /// ``` /// # Raw SQL /// ```sql /// ORDER BY User.email IS NULL, User.email ASC, User.firstName IS NOT NULL, User.firstName DESC /// ``` /// public func orderBy(_ field: SwifQLHybridOperator) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .order) parts.append(o: .space) parts.append(o: .by) parts.append(o: .space) parts.append(h: .random) return SwifQLableParts(parts: parts) } public func orderBy(_ fields: OrderByItem...) -> SwifQLable { orderBy(fields) } public func orderBy(_ fields: [OrderByItem]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .order) parts.append(o: .space) parts.append(o: .by) parts.append(o: .space) for (i, v) in fields.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Over.swift ================================================ // // SwifQLable+Over.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // import Foundation //MARK: Over extension SwifQLable { public var over: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .over) return SwifQLableParts(parts: parts) } /// [Learn more →](https://www.postgresqltutorial.com/postgresql-window-function/) public func over(_ query: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .over) parts.append(o: .space) parts.append(o: .openBracket) parts.append(contentsOf: query.parts) parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } /// [Learn more →](https://www.postgresqltutorial.com/postgresql-window-function/) public func over(partitionBy partition_expression: SwifQLable, orderBy: OrderByItem...) -> SwifQLable { over(partitionBy: partition_expression, orderBy: orderBy) } /// [Learn more →](https://www.postgresqltutorial.com/postgresql-window-function/) public func over(partitionBy partition_expression: SwifQLable, orderBy: [OrderByItem]) -> SwifQLable { var query = SwifQL.partition(by: partition_expression) if orderBy.count > 0 { query = query.orderBy(orderBy) } return over(query) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Overlaps.swift ================================================ // // SwifQLable+Overlaps.swift // SwifQL // // Created by Mihael Isaev on 02/08/2019. // import Foundation //MARK: Overlaps extension SwifQLable { public var overlaps: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .overlaps) return SwifQLableParts(parts: parts) } public func overlaps(_ fields: SwifQLable...) -> SwifQLable { overlaps(fields) } public func overlaps(_ fields: [SwifQLable]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .overlaps) if fields.count > 0 { parts.append(o: .space) parts.append(o: .openBracket) } for (i, v) in fields.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } if fields.count > 0 { parts.append(o: .closeBracket) } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Owner.swift ================================================ // // SwifQLable+Owner.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // import Foundation extension SwifQLable { public var owner: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .owner) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+PartitionBy.swift ================================================ // // SwifQLable+PartitionBy.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // import Foundation //MARK: Partition By extension SwifQLable { public var partition: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .partition) return SwifQLableParts(parts: parts) } public var by: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .by) return SwifQLableParts(parts: parts) } public func partition(by expression: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .partition) parts.append(o: .space) parts.append(o: .by) parts.append(o: .space) parts.append(contentsOf: expression.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Primary.swift ================================================ // // SwifQLable+Primary.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation //MARK: PRIMARY extension SwifQLable { public var primary: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .primary) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Raw.swift ================================================ // // SwifQLable+Raw.swift // SwifQL // // Created by Mihael Isaev on 14/02/2019. // import Foundation //MARK: Ability to append anything to query as a raw string extension SwifQLable { public func raw(_ anything: String) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .custom(anything)) return SwifQLableParts(parts: parts) } public static func raw(_ anything: String) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(o: .space) parts.append(safe: value) return SwifQLableParts(parts: parts) } } extension String { public var raw: SwifQLable { SwifQL.raw(self) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+References.swift ================================================ // // SwifQLable+References.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // import Foundation //MARK: REFERENCES extension SwifQLable { public var references: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .references) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Rename.swift ================================================ // // SwifQLable+Rename.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // import Foundation extension SwifQLable { public var rename: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .rename) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Restrict.swift ================================================ // // SwifQLable+Restrict.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // import Foundation //MARK: RESTRICT extension SwifQLable { public var restrict: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .restrict) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Return.swift ================================================ // // SwifQLable+Return.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation extension SwifQLable { public var `return`: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .return) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Returning.swift ================================================ // // SwifQLable+Returning.swift // SwifQL // // Created by Mihael Isaev on 11/07/2019. // import Foundation extension SwifQLable { public var returning: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .returning) return SwifQLableParts(parts: parts) } public func returning(_ paths: KeyPathLastPath...) -> SwifQLable { returning(paths) } public func returning(_ paths: [KeyPathLastPath]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .returning) parts.append(o: .space) for (i, p) in paths.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(SwifQLPartAlias(p.lastPath)) } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Rollback.swift ================================================ // // SwifQLable+Rollback.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation //MARK: ROLLBACK extension SwifQLable { public var rollback: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .rollback) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Schema.swift ================================================ // // SwifQLable+Schema.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // import Foundation extension SwifQLable { public var schema: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .schema) return SwifQLableParts(parts: parts) } public func schema(_ name: String) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .schema) parts.append(o: .space) parts.append(SwifQLPartSchema(name)) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Select.swift ================================================ // // SwifQLable+Select.swift // SwifQLCore // // Created by Mihael Isaev on 13/11/2018. // import Foundation //MARK: Select extension SwifQLable { public var select: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .select) return SwifQLableParts(parts: parts) } public func select(_ fields: SwifQLable...) -> SwifQLable { select(fields) } public func select(_ fields: [SwifQLable]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .select) parts.append(o: .space) for (i, v) in fields.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Semicolon.swift ================================================ // // SwifQLable+Semicolon.swift // // // Created by Mihael Isaev on 24.01.2020. // import Foundation extension SwifQLable { /// Represent just `;` symbol public var semicolon: SwifQLable { var parts: [SwifQLPart] = self.parts parts.append(o: .semicolon) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Set.swift ================================================ // // SwifQLable+Set.swift // // // Created by Mihael Isaev on 26.01.2020. // import Foundation //MARK: SET extension SwifQLable { public var set: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .set) return SwifQLableParts(parts: parts) } public func set(_ predicates: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .set) parts.append(o: .space) parts.append(contentsOf: predicates.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Space.swift ================================================ // // SwifQLable+Space.swift // SwifQL // // Created by Mihael Isaev on 31.01.2020. // import Foundation //MARK: simpel whitespace extension SwifQLable { public var space: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .space) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Subscript.swift ================================================ // // SwifQLable+Subscript.swift // SwifQL // // Created by Mihael Isaev on 20/03/2019. // import Foundation extension SwifQLable { /// Gives ability to append something wrapped into square brackets /// # Example /// ```swift /// Fn.array_agg(Fn.to_jsonb("Attachment"))[1] /// ``` /// # SQL representation /// ``` /// array_agg(to_jsonb("Attachment"))[1] /// ``` public subscript (_ items: SwifQLable) -> SwifQLable { var parts = self.parts parts.append(o: .openSquareBracket) parts.append(contentsOf: items.parts) parts.append(o: .closeSquareBracket) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Table.swift ================================================ // // SwifQLable+Table.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation extension SwifQLable { public var table: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .table) return SwifQLableParts(parts: parts) } public func table(_ name: String) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .table) parts.append(o: .space) parts.append(SwifQLPartTable(name)) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Timestamp.swift ================================================ // // SwifQLable+Timestamp.swift // SwifQL // // Created by Mihael Isaev on 02/08/2019. // import Foundation //MARK: Timestamp extension SwifQLable { public var timestamp: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .timestamp) return SwifQLableParts(parts: parts) } public func timestamp(_ fields: SwifQLable...) -> SwifQLable { timestamp(fields) } public func timestamp(_ fields: [SwifQLable]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .timestamp) parts.append(o: .space) for (i, v) in fields.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+To.swift ================================================ // // SwifQLable+To.swift // SwifQL // // Created by Mihael Isaev on 12.04.2020. // import Foundation extension SwifQLable { public var to: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .to) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Type.swift ================================================ // // SwifQLable+Type.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation extension SwifQLable { public var type: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .type) return SwifQLableParts(parts: parts) } public func type(_ name: String) -> SwifQLable { type(nil, name) } public func type(_ schema: String?, _ name: String) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .type) if let schema = schema { parts.append(o: .space) parts.append(contentsOf: Path.Schema(schema).table(name).parts) } else { parts.append(o: .space) parts.append(contentsOf: Path.Table(name).parts) } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Union.swift ================================================ // // SwifQLable+Union.swift // SwifQLCore // // Created by Mihael Isaev on 16/11/2018. // import Foundation //MARK: Union extension SwifQLable { public var union: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .union) parts.append(o: .space) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Unique.swift ================================================ // // SwifQLable+Unique.swift // // // Created by Mihael Isaev on 25.01.2020. // import Foundation //MARK: UNIQUE extension SwifQLable { public var unique: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .unique) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Update.swift ================================================ // // SwifQLable+Update.swift // SwifQL // // Created by Mihael Isaev on 26/11/2018. // import Foundation //MARK: UPDATE extension SwifQLable { public var update: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .update) return SwifQLableParts(parts: parts) } public func update(_ tables: SwifQLable...) -> SwifQLable { update(tables) } public func update(_ tables: [SwifQLable]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .update) parts.append(o: .space) for (i, v) in tables.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Value.swift ================================================ // // SwifQLable+Value.swift // SwifQL // // Created by Mihael Isaev on 27.01.2020. // import Foundation extension SwifQLable { public subscript (any item: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(contentsOf: item.parts) return SwifQLableParts(parts: parts) } /// Represent just `VALUE` keyword public var value: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .value) return SwifQLableParts(parts: parts) } /// Represent `VALUE _` where _ is provided item public func value(_ item: SwifQLable) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .value) parts.append(o: .space) parts.append(contentsOf: item.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Values.swift ================================================ // // SwifQLable+Values.swift // App // // Created by Mihael Isaev on 25/11/2018. // import Foundation extension SwifQLable { public subscript (values items: SwifQLable...) -> SwifQLable { values(items) } public subscript (values items: [SwifQLable]) -> SwifQLable { values(items) } /// Represent just `VALUES` keyword public var values: SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .values) return SwifQLableParts(parts: parts) } /// Represent provided values in round brackets separated with comma public func values(_ items: SwifQLable...) -> SwifQLable { values(items) } /// Represent provided values in round brackets separated with comma public func values(_ items: [SwifQLable]) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() parts.append(o: .openBracket) for (i, v) in items.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } /// e.g. INSERT INTO CarBrands (name) VALUES ("Acura"), ("Audi"), ("BMW") public func values(array: [SwifQLable]...) -> SwifQLable { values(array: array) } /// e.g. INSERT INTO CarBrands (name) VALUES ("Acura"), ("Audi"), ("BMW") public func values(array: [[SwifQLable]]) -> SwifQLable { var parts: [SwifQLPart] = self.parts parts.appendSpaceIfNeeded() for (i, v) in array.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(o: .openBracket) for (i, v) in v.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } parts.append(o: .closeBracket) } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Where.swift ================================================ // // SwifQLable+Where.swift // SwifQLCore // // Created by Mihael Isaev on 13/11/2018. // import Foundation //MARK: Where extension SwifQLable { public var `where`: SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .where) return SwifQLableParts(parts: parts) } public func `where`(_ predicates: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .where) parts.append(o: .space) parts.append(contentsOf: predicates.parts) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+WhereExists.swift ================================================ // // SwifQLable+WhereExists.swift // SwifQL // // Created by Mihael Isaev on 23/07/2019. // import Foundation //MARK: Where Exists extension SwifQLable { public func whereExists(_ predicates: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .where) parts.append(o: .space) parts.append(o: .exists) parts.append(o: .space) parts.append(o: .openBracket) parts.append(contentsOf: predicates.parts) parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+WhereNotExists.swift ================================================ // // SwifQLable+WhereNotExists.swift // SwifQL // // Created by Mihael Isaev on 23/07/2019. // import Foundation //MARK: Where Not Exists extension SwifQLable { public func whereNotExists(_ predicates: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .where) parts.append(o: .space) parts.append(o: .not) parts.append(o: .space) parts.append(o: .exists) parts.append(o: .space) parts.append(o: .openBracket) parts.append(contentsOf: predicates.parts) parts.append(o: .closeBracket) return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+Window.swift ================================================ // // SwifQLable+Window.swift // SwifQL // // Created by Mihael Isaev on 22.05.2020. // import Foundation //MARK: Window extension SwifQLable { /// [Learn more →](https://www.postgresqltutorial.com/postgresql-window-function/) public func window(_ expression: SwifQLable) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .window) parts.append(o: .space) parts.append(contentsOf: expression.parts) return SwifQLableParts(parts: parts) } /// [Learn more →](https://www.postgresqltutorial.com/postgresql-window-function/) public func window(_ window: SwifQLable, as query: SwifQLable) -> SwifQLable { var parts = window.parts parts.append(contentsOf: window.parts) parts.append(o: .space) parts.append(o: .as) parts.append(o: .space) parts.append(o: .openBracket) parts.append(contentsOf: query.parts) parts.append(o: .closeBracket) return self.window(SwifQLableParts(parts: parts)) } /// [Learn more →](https://www.postgresqltutorial.com/postgresql-window-function/) public func window(_ window: SwifQLable, asPartitionBy expression: SwifQLable, orderBy: OrderByItem...) -> SwifQLable { self.window(window, asPartitionBy: expression, orderBy: orderBy) } /// [Learn more →](https://www.postgresqltutorial.com/postgresql-window-function/) public func window(_ window: SwifQLable, asPartitionBy expression: SwifQLable, orderBy: [OrderByItem]) -> SwifQLable { var query = SwifQL.partition(by: expression) if orderBy.count > 0 { query = query.orderBy(orderBy) } return self.window(window, as: query) } } ================================================ FILE: Sources/SwifQL/SwifQLable+Parts/SwifQLable+With.swift ================================================ // // SwifQLable+Timestamp.swift // SwifQL // // Created by Mihael Isaev on 02/08/2019. // import Foundation //MARK: With /// SELECT in WITH /// WITH provides a way to write auxiliary statements for use in a larger query. /// These statements, which are often referred to as Common Table Expressions or CTEs, /// can be thought of as defining temporary tables that exist just for one query. /// Each auxiliary statement in a WITH clause can be a SELECT, INSERT, UPDATE, or DELETE; /// and the WITH clause itself is attached to a primary statement that can also be a /// SELECT, INSERT, UPDATE, or DELETE. /// ``` /// SwifQL /// .with(.init(Table("Table1"), SwifQL.select(Table("Table2").*).from(Table("Table2")))) /// .select(Table("Table1").*) /// .from(Table("Table1")) /// ``` /// Result /// ``` /// WITH "Table1" as (SELECT "Table2".* FROM "Table2") SELECT "Table1".* FROM "Table1" /// ``` /// https://www.postgresql.org/docs/11/queries-with.html /// extension SwifQLable { public func with(_ withs: With...) -> SwifQLable { with(withs) } public func with(_ withs: [With]) -> SwifQLable { var parts = self.parts parts.appendSpaceIfNeeded() parts.append(o: .with) parts.append(o: .space) for (i, v) in withs.enumerated() { if i > 0 { parts.append(o: .comma) parts.append(o: .space) } parts.append(contentsOf: v.parts) } return SwifQLableParts(parts: parts) } } ================================================ FILE: Sources/SwifQL/SwifQLable.swift ================================================ // // SwifQLable.swift // SwifQL // // Created by Mihael Isaev on 04/11/2018. // import Foundation public protocol SwifQLable: CustomStringConvertible { var parts: [SwifQLPart] { get } } extension SwifQLable { public var description: String { prepare(.psql).plain } } public struct SwifQLableParts: SwifQLable { public var parts: [SwifQLPart] public init (parts: SwifQLPart...) { self.init(parts: parts) } public init (parts: [SwifQLPart]) { self.parts = parts } } public protocol SwifQLPart {} public protocol SwifQLKeyPathable: SwifQLPart { var schema: String? { get } var table: String? { get } var paths: [String] { get } } extension SwifQLable { /// Good choice only for super short and universal queries like `BEGIN;`, `ROLLBACK;`, `COMMIT;` public func prepare() -> SwifQLPrepared { prepare(.any) } public func prepare(_ dialect: SQLDialect) -> SwifQLPrepared { var values: [Encodable] = [] var formattedValues: [String] = [] let query = parts.map { part in switch part { case let v as SwifQLPartArray: guard v.elements.count > 0 else { return dialect.emptyArrayStart + dialect.emptyArrayEnd } var string = dialect.arrayStart v.elements.enumerated().forEach { i, v in if i > 0 { string += dialect.arraySeparator } let prepared = v.prepare(dialect) values.append(contentsOf: prepared._values) formattedValues.append(contentsOf: prepared._formattedValues) string += prepared._query } return string + dialect.arrayEnd case let v as SwifQLPartBool: return dialect.boolValue(v.value) case is SwifQLPartNull: return dialect.null case let v as SwifQLPartSchema: guard let schema = v.schema else { return "" } return dialect.schemaName(schema) case let v as SwifQLPartTable: if let schema = v.schema { return dialect.schemaName(schema) + "." + dialect.tableName(v.table) } return dialect.tableName(v.table) case let v as SwifQLPartTableWithAlias: if let schema = v.schema { return dialect.schemaName(schema) + "." + dialect.tableName(v.table, andAlias: v.alias) } return dialect.tableName(v.table, andAlias: v.alias) case let v as SwifQLPartAlias: return dialect.alias(v.alias) case let v as SwifQLPartKeyPath: return dialect.keyPath(v) case let v as SwifQLPartColumn: return dialect.column(v.name) case let v as SwifQLPartOperator: return v._value case let v as SwifQLHybridOperator: switch dialect{ case .psql: return v._psql._value case .mysql: return v._mysql._value default: return v._mysql._value } case let v as SwifQLPartDate: return dialect.date(v.date) case let v as SwifQLPartSafeValue: return dialect.safeValue(v.safeValue) case let v as SwifQLPartUnsafeValue: values.append(v.unsafeValue) formattedValues.append(dialect.safeValue(v.unsafeValue)) return dialect.bindSymbol default: return "" } }.joined(separator: "") return .init(dialect: dialect, query: query, values: values, formattedValues: formattedValues) } } ================================================ FILE: Sources/SwifQL/SwifQLableArraySeparator.swift ================================================ // // SwifQLableArraySeparator.swift // // // Created by Mihael Isaev on 26.01.2020. // import Foundation public enum SwifQLableArraySeparator { case comma var `operator`: SwifQLPartOperator { switch self { case .comma: return .comma } } } ================================================ FILE: Sources/SwifQL/Table.swift ================================================ // // Tableable.swift // SwifQL // // Created by Mihael Isaev on 05/11/2018. // import Foundation public protocol AnyTable: Codable { /// This model's unique name. By default, this property is set to a `String` describing the type. static var tableName: String { get } } extension AnyTable { public static var tableName: String { String(describing: Self.self) } public static func column(_ paths: String...) -> Path.SchemaWithTableAndColumn { Path.Schema((Self.self as? Schemable.Type)?.schemaName).table(tableName).column(paths) } public static func inSchema(_ name: String) -> Schema { .init(name) } public static func inSchema(_ schema: Schemable.Type) -> Schema { .init(schema.schemaName) } } public protocol KeyPathEncodable {} @dynamicMemberLookup public protocol Table: AnyTable, ColumnRoot { @_disfavoredOverload init () static subscript(dynamicMember keyPath: KeyPath) -> SwifQLable { get } } extension Table { public static subscript(dynamicMember keyPath: KeyPath) -> SwifQLable { guard let k = keyPath as? Keypathable else { return "" } let schema: String? = (Self.self as? Schemable.Type)?.schemaName return SwifQLPartKeyPath(schema: schema, table: Self.tableName, paths: k.paths) } } public struct ColumnInfo { public struct Name { public let label, keyPath: String } public let name: Name public let property: AnyColumn } extension String { fileprivate var withoutLeadingUnderscore: String { guard hasPrefix("_") else { return self } return String(self.dropFirst()) } } extension Table { public var columns: [ColumnInfo] { return Mirror(reflecting: self) .children .compactMap { child in guard let property = child.value as? AnyColumn else { return nil } // remove underscore return .init(name: .init(label: property.name, keyPath: child.label?.withoutLeadingUnderscore ?? property.name), property: property) } } /// See `Codable` public init(from decoder: Decoder) throws { self.init() let container = try decoder.container(keyedBy: TableCodingKey.self) try self.columns.forEach { let decoder = TableContainerDecoder(container: container, key: .string($0.name.label)) try $0.property.decode(from: decoder) } } public func encode(to encoder: Encoder) throws { let container = encoder.container(keyedBy: TableCodingKey.self) for column in columns { let key: TableCodingKey switch self { case _ as KeyPathEncodable: key = .string(column.name.keyPath) default: key = .string(column.name.label) } let encoder = ContainerEncoder(container: container, key: key) try column.property.encode(to: encoder) } } } enum TableCodingKey: CodingKey { case string(String) case int(Int) var stringValue: String { switch self { case .int(let int): return String(describing: int) case .string(let string): return string } } var intValue: Int? { switch self { case .int(let int): return int case .string(let string): return Int(string) } } init?(stringValue: String) { self = .string(stringValue) } init?(intValue: Int) { self = .int(intValue) } } private struct TableContainerDecoder: Decoder, SingleValueDecodingContainer { let container: KeyedDecodingContainer let key: TableCodingKey var codingPath: [CodingKey] { self.container.codingPath } var userInfo: [CodingUserInfoKey : Any] { [:] } func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { try self.container.nestedContainer(keyedBy: Key.self, forKey: self.key) } func unkeyedContainer() throws -> UnkeyedDecodingContainer { try self.container.nestedUnkeyedContainer(forKey: self.key) } func singleValueContainer() throws -> SingleValueDecodingContainer { self } func decode(_ type: T.Type) throws -> T where T : Decodable { try self.container.decode(T.self, forKey: self.key) } func decodeNil() -> Bool { do { return try self.container.decodeNil(forKey: self.key) } catch { return true } } } private struct ContainerEncoder: Encoder, SingleValueEncodingContainer { var container: KeyedEncodingContainer let key: TableCodingKey var codingPath: [CodingKey] { self.container.codingPath } var userInfo: [CodingUserInfoKey : Any] { [:] } func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey { var container = self.container return container.nestedContainer(keyedBy: Key.self, forKey: self.key) } func unkeyedContainer() -> UnkeyedEncodingContainer { var container = self.container return container.nestedUnkeyedContainer(forKey: self.key) } func singleValueContainer() -> SingleValueEncodingContainer { self } mutating func encode(_ value: T) throws where T : Encodable { try self.container.encode(value, forKey: self.key) } mutating func encodeNil() throws { try self.container.encodeNil(forKey: self.key) } } extension Table { public static func key(for column: KeyPath) -> String where Column: ColumnRepresentable { Self.init()[keyPath: column].column.name } } ================================================ FILE: Sources/SwifQL/TableAlias.swift ================================================ // // TableAlias.swift // SwifQL // // Created by Mihael Isaev on 11/11/2018. // import Foundation /// Create alias for `Table` /// /// Usage: /// * using `TableAlias` for _subquery_ /// ```swift /// let u = TableAlias("u") /// let subquery = |SwifQL /// .select(Fn.count(\User.$id) => "users", /// \User.$groupID => "groupID") /// .from(User.table) /// .groupBy(\User.$groupID)| => u /// let query = SwifQL.select(..., u.users) /// .from(...) /// .join(.left, subquery, on: u.groupID == \Group.$id) /// .groupBy(..., u.users) /// ``` @dynamicMemberLookup public class TableAlias: SwifQLable { public var name: String public init (_ name: String) { self.name = name } public subscript(dynamicMember path: String) -> SwifQLable { Path.Table(name).column(path) } public var parts: [SwifQLPart] { [SwifQLPartAlias(name)] } } protocol AnyGenericTableAlias { var alias: String { get } } @dynamicMemberLookup public class GenericTableAlias: SwifQLable, AnyGenericTableAlias { public typealias Model = M public var parts: [SwifQLPart] { [SwifQLPartTable(schema: nil, table: alias)] } public var table: SwifQLable { SwifQLableParts(parts: SwifQLPartTableWithAlias(schema: schema, table: name, alias: alias)) } var name: String { if let mmm = M.self as? AnyTable.Type { return mmm.tableName } return String(describing: M.self) } var schema: String? var alias: String public init(_ alias: String, schema: String?) { self.alias = alias self.schema = schema ?? (Model.self as? Schemable.Type)?.schemaName } public func column(_ paths: String...) -> Path.TableWithColumn { Path.Table(alias).column(paths) } public subscript(dynamicMember keyPath: KeyPath) -> SwifQLable { guard let k = keyPath as? Keypathable else { return "" } return Path.Table(alias).column(k.paths) } } postfix operator * postfix public func *(table: GenericTableAlias) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(SwifQLPartTable(table.alias)) parts.append(o: .custom(".*")) parts.append(o: .space) return SwifQLableParts(parts: parts) } postfix public func *(table: AnyTable.Type) -> SwifQLable { var parts: [SwifQLPart] = [] parts.append(SwifQLPartTable(table.tableName)) parts.append(o: .custom(".*")) parts.append(o: .space) return SwifQLableParts(parts: parts) } //MARK: Decodable extension extension Decodable { public static func `as`(_ alias: String) -> GenericTableAlias { .init(alias, schema: nil) } } //MARK: AliasedKeyPath public class AliasedKeyPath where K: KeyPath, T: Table, V: ColumnRepresentable { var alias: String var kp: K init(_ alias: String, _ kp: K) { self.alias = alias self.kp = kp } } extension AliasedKeyPath: SwifQLKeyPathable { public var schema: String? { (AModel.self as? Schemable.Type)?.schemaName } public var table: String? { alias } public var paths: [String] { kp.paths } } extension AliasedKeyPath: SwifQLable { public var parts: [SwifQLPart] { if let kp = self.originalKeyPath as? FluentKitFieldable { return [SwifQLPartKeyPath(table: table, paths: [kp.key])] } return [SwifQLPartKeyPath(table: table, paths: paths)] } } extension AliasedKeyPath: SwifQLUniversalKeyPath, SwifQLUniversalKeyPathSimple { public typealias AType = V public typealias AModel = T public typealias ARoot = AliasedKeyPath public var path: String { kp.shortPath } public var lastPath: String { kp.lastPath } public var originalKeyPath: KeyPath { kp } } ================================================ FILE: Sources/SwifQL/Type+Autodetect.swift ================================================ // // Type+Autodetect.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // import Foundation extension Type { public static func auto(from type: T.Type, isPrimary: Bool = false) -> Type { switch type { case is Optional.Type: fallthrough case is String.Type: return .text case is Optional<[String]>.Type: fallthrough case is [String].Type: return .textArray case is Optional.Type: fallthrough case is UUID.Type: return .uuid case is Optional<[UUID]>.Type: fallthrough case is [UUID].Type: return .uuidArray case is Optional.Type: fallthrough case is Double.Type: return .decimal case is Optional<[Double]>.Type: fallthrough case is [Double].Type: return .decimalArray case is Optional.Type: fallthrough case is Float.Type: return .float4 case is Optional<[Float]>.Type: fallthrough case is [Float].Type: return .float4Array case is Optional.Type: fallthrough case is UInt.Type: fallthrough case is Optional.Type: fallthrough case is UInt8.Type: fallthrough case is Optional.Type: fallthrough case is UInt16.Type: fallthrough case is Optional.Type: fallthrough case is UInt32.Type: fallthrough case is Optional.Type: fallthrough case is UInt64.Type: return .int case is Optional<[UInt]>.Type: fallthrough case is [UInt].Type: fallthrough case is Optional<[UInt8]>.Type: fallthrough case is [UInt8].Type: fallthrough case is Optional<[UInt16]>.Type: fallthrough case is [UInt16].Type: fallthrough case is Optional<[UInt32]>.Type: fallthrough case is [UInt32].Type: fallthrough case is Optional<[UInt64]>.Type: fallthrough case is [UInt64].Type: return .intArray case is Optional.Type: fallthrough case is Int8.Type: fallthrough case is Optional.Type: fallthrough case is Int16.Type: fallthrough case is Optional.Type: fallthrough case is Int32.Type: fallthrough case is Optional.Type: fallthrough case is Int.Type: if isPrimary { return .serial } else { return .int } case is Optional.Type: fallthrough case is Int64.Type: if isPrimary { return .bigserial } else { return .bigint } case is Optional<[Int8]>.Type: fallthrough case is [Int8].Type: fallthrough case is Optional<[Int16]>.Type: fallthrough case is [Int16].Type: fallthrough case is Optional<[Int32]>.Type: fallthrough case is [Int32].Type: fallthrough case is Optional<[Int]>.Type: fallthrough case is [Int].Type: return .intArray case is Optional<[Int64]>.Type: fallthrough case is [Int64].Type: return .bigintArray case is Optional.Type: fallthrough case is Date.Type: return .timestamptz case is Optional<[Date]>.Type: fallthrough case is [Date].Type: return .timestamptzArray case is Optional.Type: fallthrough case is Data.Type: return .bytea case is Optional<[Data]>.Type: fallthrough case is [Data].Type: return .byteaArray case is AnyOptionalEnum.Type: guard let t = type as? AnyOptionalEnum.Type else { fallthrough } if let st = type as? Schemable.Type { return .custom(st.schemaName + "." + t.name) } else { return .custom(t.name) } case is AnyOptionalEnumArray.Type: guard let t = type as? AnyOptionalEnumArray.Type else { fallthrough } if let st = type as? Schemable.Type { return .customArray(st.schemaName + "." + t.name) } else { return .customArray(t.name) } case is AnySwifQLEnum.Type: guard let t = type as? AnySwifQLEnum.Type else { fallthrough } if let st = type as? Schemable.Type { return .custom(st.schemaName + "." + t.name) } else { return .custom(t.name) } case is _AnySwifQLEnumArray.Type: guard let t = type as? _AnySwifQLEnumArray.Type else { fallthrough } if let st = t.elementType as? Schemable.Type { return .customArray(st.schemaName + "." + t.name) } else { return .customArray(t.name) } case is Optional<[Encodable]>.Type: fallthrough case is [Encodable].Type: return .jsonbArray case is ClosedRange.Type: return .daterange case is Range.Type: return .daterange default: return .text } } } fileprivate protocol AnyOptionalEnum { static var name: String { get } } extension Optional: AnyOptionalEnum where Wrapped: AnySwifQLEnum { fileprivate static var name: String { return Wrapped.name } } extension Optional { fileprivate static var trololo: Wrapped.Type { return Wrapped.self } } fileprivate protocol AnyOptionalEnumArray { static var name: String { get } } extension Array: AnyOptionalEnumArray where Element: AnyOptionalEnum { fileprivate static var name: String { Element.name } } fileprivate protocol _AnySwifQLEnumArray { static var name: String { get } static var elementType: AnySwifQLEnum.Type { get } } extension Array: _AnySwifQLEnumArray where Element: AnySwifQLEnum { fileprivate static var name: String { Element.name } fileprivate static var elementType: AnySwifQLEnum.Type { Element.self } } ================================================ FILE: Sources/SwifQL/Type+SwifQLable.swift ================================================ // // SwifQLable+Types.swift // SwifQL // // Created by Mihael Isaev on 04/11/2018. // import Foundation extension Optional: SwifQLable, CustomStringConvertible where Wrapped: SwifQLable { public var parts: [SwifQLPart] { switch self { case .none: return [SwifQLPartSafeValue(nil)] case .some(let value): return value.parts } } } extension String: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension UUID: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension Decimal: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension Double: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension Float: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension UInt: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension UInt8: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension UInt16: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension UInt32: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension UInt64: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension Int: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension Int8: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension Int16: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension Int32: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension Int64: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartUnsafeValue(self)] } } extension Date: SwifQLable { public var parts: [SwifQLPart] { [SwifQLPartDate(self)] } } extension Data: SwifQLable { public var parts: [SwifQLPart] { return [ SwifQLPartOperator("decode"), SwifQLPartOperator.openBracket, SwifQLPartSafeValue(base64EncodedString()), SwifQLPartOperator.comma, SwifQLPartOperator.space, SwifQLPartSafeValue("base64"), SwifQLPartOperator.closeBracket ] } } public protocol SwifQLRawRepresentable: RawRepresentable, SwifQLable {} extension SwifQLRawRepresentable { public var parts: [SwifQLPart] { if let a = self.rawValue as? SwifQLable { return a.parts } return [] } } ================================================ FILE: Sources/SwifQL/Type.swift ================================================ // // Type.swift // SwifQL // // Created by Mihael Isaev on 12/10/2019. // typealias CastType = Type public struct Type { let name: String public init (_ name: String) { self.name = name } // MARK: - /// enum public static let `enum`: Type = .init("ENUM") /// range public static let range: Type = .init("RANGE") // MARK: - // MARK: Numeric Types /// signed two-byte integer public static let smallint: Type = .init("smallint") public static let smallintArray: Type = .init("smallint[]") /// signed four-byte integer public static let int4: Type = .init("int4") public static let int4Array: Type = .init("int4[]") /// signed two-byte integer public static let int2: Type = .init("int2") public static let int2Array: Type = .init("int2[]") /// signed eight-byte integer public static let int8: Type = .init("int8") public static let int8Array: Type = .init("int8[]") /// signed four-byte integer public static let int: Type = .init("int") public static let intArray: Type = .init("int[]") /// signed four-byte integer public static let integer: Type = .init("integer") public static let integerArray: Type = .init("integer[]") /// signed eight-byte integer public static let bigint: Type = .init("bigint") public static let bigintArray: Type = .init("bigint[]") /// exact numeric of selectable precision public static let decimal: Type = .init("decimal") public static let decimalArray: Type = .init("decimal[]") /// exact numeric of selectable precision public static func decimal(_ p: Int, _ s: Int) -> Type { .init("decimal(\(p), \(s))") } /// exact numeric of selectable precision public static let numeric: Type = .init("numeric") public static let numericArray: Type = .init("numeric[]") /// exact numeric of selectable precision public static func numeric(_ p: Int, _ s: Int) -> Type { .init("numeric(\(p), \(s))") } /// single precision floating-point number (4 bytes) public static let real: Type = .init("real") public static let realArray: Type = .init("real[]") public static func float(_ v: Int) -> Type { .init("float(\(v))") } /// single precision floating-point number (4 bytes) public static let float4: Type = .init("float4") public static let float4Array: Type = .init("float4[]") /// double precision floating-point number (8 bytes) public static let float8: Type = .init("float8") public static let float8Array: Type = .init("float8[]") /// double precision floating-point number (8 bytes) public static let doublePrecision: Type = .init("double precision") public static let doublePrecisionArray: Type = .init("double precision[]") /// autoincrementing two-byte integer public static let smallserial: Type = .init("smallserial") public static let smallserialArray: Type = .init("smallserial[]") /// autoincrementing two-byte integer public static let serial2: Type = .init("serial2") public static let serial2Array: Type = .init("serial2[]") /// autoincrementing four-byte integer public static let serial: Type = .init("serial") public static let serialArray: Type = .init("serial[]") /// autoincrementing four-byte integer public static let serial4: Type = .init("serial4") public static let serial4Array: Type = .init("serial4[]") /// autoincrementing eight-byte integer public static let serial8: Type = .init("serial8") public static let serial8Array: Type = .init("serial8[]") /// autoincrementing eight-byte integer public static let bigserial: Type = .init("bigserial") public static let bigserialArray: Type = .init("bigserial[]") // MARK: Monetary Types /// currency amount public static let money: Type = .init("money") public static let moneyArray: Type = .init("money[]") // MARK: Character Types /// fixed-length character string public static let char: Type = .init("char") public static let charArray: Type = .init("char[]") /// variable-length character string public static let varchar: Type = .init("varchar") public static func varchar(_ n: Int) -> Type { .init("varchar(\(n))") } public static let varcharArray: Type = .init("varchar[]") /// variable-length character string public static let text: Type = .init("text") public static let textArray: Type = .init("text[]") // MARK: Binary Data Types /// binary data (“byte array”) public static let bytea: Type = .init("bytea") public static let byteaArray: Type = .init("bytea[]") // MARK: Date/Time Types /// date and time (no time zone) public static let timestamp: Type = .init("timestamp") public static let timestampArray: Type = .init("timestamp[]") /// date and time (no time zone) public static let timestampWithoutTimeZone: Type = .init("timestamp without time zone") public static let timestampWithoutTimeZoneArray: Type = .init("timestamp without time zone[]") /// date and time, including time zone public static let timestampWithTimeZone: Type = .init("timestamp with time zone") public static let timestampWithTimeZoneArray: Type = .init("timestamp with time zone[]") /// date and time, including time zone public static let timestamptz: Type = .init("timestamptz") public static let timestamptzArray: Type = .init("timestamptz[]") /// date and time public static func timestamp(_ p: Int, withTimeZone: Bool = false) -> Type { return .init("timestamp(\(p)) \(withTimeZone ? "without time zone" : "with time zone")") } /// calendar date (year, month, day) public static let date: Type = .init("date") public static let dateArray: Type = .init("date[]") /// time of day (no time zone) public static let time: Type = .init("time") public static let timeArray: Type = .init("time[]") /// time of day (no time zone) public static let timeWithoutTimeZone: Type = .init("time without time zone") public static let timeWithoutTimeZoneArray: Type = .init("time without time zone[]") /// time of day, including time zone public static let timeWithTimeZone: Type = .init("time with time zone") public static let timeWithTimeZoneArray: Type = .init("time with time zone[]") /// time of day, including time zone public static let timetz: Type = .init("timetz") public static let timetzArray: Type = .init("timetz[]") /// time of day public static func time(_ p: Int, withTimeZone: Bool = false) -> Type { return .init("time(\(p)) \(withTimeZone ? "without time zone" : "with time zone")") } /// time span public static let interval: Type = .init("interval") public static let intervalArray: Type = .init("interval[]") /// time span public static func interval(_ fields: String, p: Int) -> Type { return .init("interval \(fields) (\(p))") } // MARK: Boolean Type /// logical Boolean (true/false) public static let boolean: Type = .init("boolean") public static let booleanArray: Type = .init("boolean[]") /// logical Boolean (true/false) public static let bool: Type = .init("bool") public static let boolArray: Type = .init("bool[]") // MARK: Geometric Types /// geometric point on a plane public static let point: Type = .init("point") public static let pointArray: Type = .init("point[]") /// infinite line on a plane public static let line: Type = .init("line") public static let lineArray: Type = .init("line[]") /// line segment on a plane public static let lseg: Type = .init("lseg") public static let lsegArray: Type = .init("lseg[]") /// rectangular box on a plane public static let box: Type = .init("box") public static let boxArray: Type = .init("box[]") /// geometric path on a plane public static let path: Type = .init("path") public static let pathArray: Type = .init("path[]") /// closed geometric path on a plane public static let polygon: Type = .init("polygon") public static let polygonArray: Type = .init("polygon[]") /// circle on a plane public static let circle: Type = .init("circle") public static let circleArray: Type = .init("circle[]") // MARK: Network Address Types /// IPv4 or IPv6 network address public static let cidr: Type = .init("cidr") public static let cidrArray: Type = .init("cidr[]") /// IPv4 or IPv6 host address public static let inet: Type = .init("inet") public static let inetArray: Type = .init("inet[]") /// MAC (Media Access Control) address public static let macaddr: Type = .init("macaddr") public static let macaddrArray: Type = .init("macaddr[]") /// MAC (Media Access Control) address (EUI-64 format) public static let macaddr8: Type = .init("macaddr8") public static let macaddr8Array: Type = .init("macaddr8[]") // MARK: Bit String Types /// fixed-length bit string public static let bit: Type = .init("bit") public static let bitArray: Type = .init("bit[]") /// fixed-length bit string public static func bit(_ v: Int) -> Type { .init("bit(\(v))") } /// variable-length bit string public static let bitVarying: Type = .init("bit varying") public static let bitVaryingArray: Type = .init("bit varying[]") /// variable-length bit string public static func bitVarying(_ v: Int) -> Type { .init("bit varying(\(v))") } /// variable-length bit string public static let varbit: Type = .init("varbit") /// variable-length bit string public static func varbit(_ v: Int) -> Type { .init("varbit(\(v))") } // MARK: Text Search Types /// text search document public static let tsvector: Type = .init("tsvector") public static let tsvectorArray: Type = .init("tsvector[]") /// text search query public static let tsquery: Type = .init("tsquery") public static let tsqueryArray: Type = .init("tsquery[]") // MARK: UUID Type /// universally unique identifier public static let uuid: Type = .init("uuid") public static let uuidArray: Type = .init("uuid[]") // MARK: XML Type /// XML data public static let xml: Type = .init("xml") public static let xmlArray: Type = .init("xml[]") // MARK: Postgres JSON Types /// textual JSON data public static let json: Type = .init("json") public static let jsonArray: Type = .init("json[]") /// binary JSON data, decomposed public static let jsonb: Type = .init("jsonb") public static let jsonbArray: Type = .init("jsonb[]") // MARK: Postgres Built-in Range Types /// Range of integer public static let int4range: Type = .init("int4range") public static let int4rangeArray: Type = .init("int4range[]") /// Range of bigint public static let int8range: Type = .init("int8range") public static let int8rangeArray: Type = .init("int8range[]") /// Range of numeric public static let numrange: Type = .init("numrange") public static let numrangeArray: Type = .init("numrange[]") /// Range of timestamp without time zone public static let tsrange: Type = .init("tsrange") public static let tsrangeArray: Type = .init("tsrange[]") /// Range of timestamp with time zone public static let tstzrange: Type = .init("tstzrange") public static let tstzrangeArray: Type = .init("tstzrange[]") /// Range of date public static let daterange: Type = .init("daterange") public static let daterangeArray: Type = .init("daterange[]") // MARK: Object Identifier Types /// numeric object identifier public static let oid: Type = .init("oid") public static let oidArray: Type = .init("oid[]") /// function name public static let regproc: Type = .init("regproc") public static let regprocArray: Type = .init("regproc[]") /// function name public static let pg_proc: Type = .init("pg_proc") public static let pg_procArray: Type = .init("pg_proc[]") /// function with argument types public static let regprocedure: Type = .init("regprocedure") public static let regprocedureArray: Type = .init("regprocedure[]") /// operator name public static let regoper: Type = .init("regoper") public static let regoperArray: Type = .init("regoper[]") /// operator name public static let pg_operator: Type = .init("pg_operator") public static let pg_operatorArray: Type = .init("pg_operator[]") /// operator with argument types public static let regoperator: Type = .init("regoperator") public static let regoperatorArray: Type = .init("regoperator[]") /// relation name public static let regclass: Type = .init("regclass") public static let regclassArray: Type = .init("regclass[]") /// relation name public static let pg_class: Type = .init("pg_class") public static let pg_classArray: Type = .init("pg_class[]") /// data type name public static let regtype: Type = .init("regtype") public static let regtypeArray: Type = .init("regtype[]") /// data type name public static let pg_type: Type = .init("pg_type") public static let pg_typeArray: Type = .init("pg_type[]") /// role name public static let regrole: Type = .init("regrole") public static let regroleArray: Type = .init("regrole[]") /// role name public static let pg_authid: Type = .init("pg_authid") public static let pg_authidArray: Type = .init("pg_authid[]") /// namespace name public static let regnamespace: Type = .init("regnamespace") public static let regnamespaceArray: Type = .init("regnamespace[]") /// namespace name public static let pg_namespace: Type = .init("pg_namespace") public static let pg_namespaceArray: Type = .init("pg_namespace[]") /// text search configuration public static let regconfig: Type = .init("regconfig") public static let regconfigArray: Type = .init("regconfig[]") /// text search configuration public static let pg_ts_config: Type = .init("pg_ts_config") public static let pg_ts_configArray: Type = .init("pg_ts_config[]") /// text search dictionary public static let regdictionary: Type = .init("regdictionary") public static let regdictionaryArray: Type = .init("regdictionary[]") /// text search dictionary public static let pg_ts_dict: Type = .init("pg_ts_dict") public static let pg_ts_dictArray: Type = .init("pg_ts_dict[]") // MARK: Postgres Log Sequence Number /// PostgreSQL Log Sequence Number public static let pgLsn: Type = .init("pg_lsn") public static let pgLsnArray: Type = .init("pg_lsn[]") // MARK: Postgres Pseudo-Types // The PostgreSQL type system contains a number of special-purpose entries that are collectively called pseudo-types. // A pseudo-type cannot be used as a column data type, but it can be used to declare a function's argument or result type. // Each of the available pseudo-types is useful in situations where a function's behavior does not correspond to // simply taking or returning a value of a specific SQL data type. /// Indicates that a function accepts any input data type. public static let any: Type = .init("any") /// Indicates that a function accepts any data type (see Section 37.2.5). public static let anyelement: Type = .init("anyelement") /// Indicates that a function accepts any array data type (see Section 37.2.5). public static let anyarray: Type = .init("anyarray") /// Indicates that a function accepts any non-array data type (see Section 37.2.5). public static let anynonarray: Type = .init("anynonarray") /// Indicates that a function accepts any enum data type (see Section 37.2.5 and Section 8.7). public static let anyenum: Type = .init("anyenum") /// Indicates that a function accepts any range data type (see Section 37.2.5 and Section 8.17). public static let anyrange: Type = .init("anyrange") /// Indicates that a function accepts or returns a null-terminated C string. public static let cstring: Type = .init("cstring") /// Indicates that a function accepts or returns a server-internal data type. public static let `internal`: Type = .init("internal") /// A procedural language call handler is declared to return language_handler. public static let language_handler: Type = .init("language_handler") /// A foreign-data wrapper handler is declared to return fdw_handler. public static let fdw_handler: Type = .init("fdw_handler") /// An index access method handler is declared to return index_am_handler. public static let index_am_handler: Type = .init("index_am_handler") /// A tablesample method handler is declared to return tsm_handler. public static let tsm_handler: Type = .init("tsm_handler") /// Identifies a function taking or returning an unspecified row type. public static let record: Type = .init("record") /// A trigger function is declared to return trigger. public static let trigger: Type = .init("trigger") /// An event trigger function is declared to return event_trigger. public static let event_trigger: Type = .init("event_trigger") /// Identifies a representation of DDL commands that is available to event triggers. public static let pg_ddl_command: Type = .init("pg_ddl_command") /// Indicates that a function returns no value. public static let void: Type = .init("void") /// Identifies a not-yet-resolved type, e.g. of an undecorated string literal. public static let unknown: Type = .init("unknown") /// An obsolete type name that formerly served many of the above purposes. public static let opaque: Type = .init("opaque") // MARK: Other /// user-level transaction ID snapshot public static let txid_snapshot: Type = .init("txid_snapshot") public static let txid_snapshotArray: Type = .init("txid_snapshot[]") // MARK: - // MARK: Array Subscript public subscript () -> Type { .init("\(name)[]") } // MARK: - // MARK: Custom public static func custom(_ name: String) -> Type { .init(name) } public static func customArray(_ name: String) -> Type { .init(name + "[]") } } ================================================ FILE: Sources/SwifQL/_Todo.swift ================================================ // // Todo.swift // SwifQL // // Created by Mihael Isaev on 29.01.2020. // // MARK: - Improve drop table // https://www.postgresql.org/docs/8.2/sql-droptable.html // MARK: - Add Functions // array functions https://w3resource.com/PostgreSQL/postgresql_array_remove-function.php ================================================ FILE: Tests/SwifQLTests/BuilderTests.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("Builder Tests") struct BuilderTests: SwifQLTests { @Test("Test Select Builder Limit Short") func selectBuilderLimitShort() { let builder = SwifQLSelectBuilder() let query = builder.select(CarBrands.table.*).from(CarBrands.table).limit(0, 10).build() check( query, .psql(#"SELECT "CarBrands".* FROM "CarBrands" LIMIT 10 OFFSET 0"#), .mysql("SELECT CarBrands.* FROM CarBrands LIMIT 10 OFFSET 0") ) } @Test("Test Select Builder Copy") func selectBuilderCopy() { let builder = SwifQLSelectBuilder() builder.select(CarBrands.table.*).from(CarBrands.table).join(.left, CarBrands.table, on: "id1 = id2").where("item1 = item2").groupBy("item1").limit(10).offset(20).having("count > 10").orderBy(.asc("item1")) let copy = builder.copy() let prepareBuildMySQL = builder.build().prepare(.mysql).plain let prepareCopyMySQL = copy.build().prepare(.mysql).plain XCTAssertEqual(prepareBuildMySQL , prepareCopyMySQL) let prepareBuildPSQL = builder.build().prepare(.psql).plain let prepareCopyPSQL = copy.build().prepare(.psql).plain XCTAssertEqual(prepareBuildPSQL , prepareCopyPSQL) } @Test("Test Update Builder Add Column") func updateBuilderAddColumn() { check( UpdateTableBuilder() .addColumn(NewColumn.init("hello", .text)), .psql(#"ALTER TABLE "CarBrands" ADD COLUMN "hello" text;"#), .mysql("ALTER TABLE CarBrands ADD COLUMN hello text;") ) check( UpdateTableBuilder() .addColumn("hello", .text), .psql(#"ALTER TABLE "CarBrands" ADD COLUMN "hello" text;"#), .mysql("ALTER TABLE CarBrands ADD COLUMN hello text;") ) } } ================================================ FILE: Tests/SwifQLTests/CaseTests.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("Case Tests") struct CaseTests: SwifQLTests { // MARK: - Case When Then Else @Test("Test Case When Then Else 1") func caseWhenThenElse1() { check( Case .when(CarBrands.column("name") == "BMW") .then("Crazy driver") .when(CarBrands.column("name") == "Tesla") .then("Fancy driver") .else("Normal driver") .end, .psql(#"CASE WHEN "CarBrands"."name" = 'BMW' THEN 'Crazy driver' WHEN "CarBrands"."name" = 'Tesla' THEN 'Fancy driver' ELSE 'Normal driver' END"#), .mysql("CASE WHEN CarBrands.name = 'BMW' THEN 'Crazy driver' WHEN CarBrands.name = 'Tesla' THEN 'Fancy driver' ELSE 'Normal driver' END") ) } @Test("Test Case When Then Else 2") func caseWhenThenElse2() { check( Case(CarBrands.column("name")) .when("BMW") .then("Crazy driver") .when("Tesla") .then("Fancy driver") .else("Normal driver") .end, .psql(#"CASE "CarBrands"."name" WHEN 'BMW' THEN 'Crazy driver' WHEN 'Tesla' THEN 'Fancy driver' ELSE 'Normal driver' END"#), .mysql("CASE CarBrands.name WHEN 'BMW' THEN 'Crazy driver' WHEN 'Tesla' THEN 'Fancy driver' ELSE 'Normal driver' END") ) } } ================================================ FILE: Tests/SwifQLTests/DirectiveTests.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("Directive Tests") struct DirectiveTests: SwifQLTests { // MARK: - UPDATE @Test("Test Update") func update() { check( SwifQL.update(Path.Schema(nil).table("ttt")), .psql(#"UPDATE "ttt""#), .mysql("UPDATE ttt") ) check( SwifQL.update(Path.Schema("sss").table("ttt")), .psql(#"UPDATE "sss"."ttt""#), .mysql("UPDATE sss.ttt") ) } // MARK: - INSERT INTO @Test("Test Insert Into") func insertInto() { check( SwifQL.insertInto(Path.Schema(nil).table("ttt"), fields: "id"), .psql(#"INSERT INTO "ttt" ("id")"#), .mysql("INSERT INTO ttt (id)") ) check( SwifQL.insertInto(Path.Schema("sss").table("ttt"), fields: "id"), .psql(#"INSERT INTO "sss"."ttt" ("id")"#), .mysql("INSERT INTO sss.ttt (id)") ) check( SwifQL.insertInto(CarBrands.table, fields: CarBrands.column("id")), .psql(#"INSERT INTO "CarBrands" ("id")"#), .mysql("INSERT INTO CarBrands (id)") ) check( SwifQL.insertInto(CarBrands.table, fields: CarBrands.column("id"), CarBrands.column("name"), CarBrands.column("createdAt")), .psql(#"INSERT INTO "CarBrands" ("id", "name", "createdAt")"#), .mysql("INSERT INTO CarBrands (id, name, createdAt)") ) check( SwifQL.insertInto(cb.table, fields: cb.column("id")), .psql(#"INSERT INTO "CarBrands" AS "cb" ("id")"#), .mysql("INSERT INTO CarBrands AS cb (id)") ) check( SwifQL.insertInto(cb.table, fields: cb.column("id"), cb.column("name"), cb.column("createdAt")), .psql(#"INSERT INTO "CarBrands" AS "cb" ("id", "name", "createdAt")"#), .mysql("INSERT INTO CarBrands AS cb (id, name, createdAt)") ) check( SwifQL.insertInto(cb.table, fields: CarBrands.column("id"), cb.column("name"), CarBrands.column("createdAt")), .psql(#"INSERT INTO "CarBrands" AS "cb" ("id", "name", "createdAt")"#), .mysql("INSERT INTO CarBrands AS cb (id, name, createdAt)") ) } // MARK: - Delete @Test("Test Delete") func delete() { check( SwifQL.delete(from: CarBrands.table).where(CarBrands.column("name") == "BMW"), .psql(#"DELETE FROM "CarBrands" WHERE "CarBrands"."name" = 'BMW'"#), .mysql("DELETE FROM CarBrands WHERE CarBrands.name = 'BMW'") ) } // MARK: - RETURNING @Test("Test Returning") func returning() { check( SwifQL.returning("hello_world", "bye_world"), .psql(#"RETURNING "hello_world", "bye_world""#), .mysql("RETURNING hello_world, bye_world") ) check( SwifQL.returning(CarBrands.column("id"), CarBrands.column("name")), .psql(#"RETURNING "id", "name""#), .mysql("RETURNING id, name") ) } // MARK: - ON CONFLICT DO NOTHING @Test("Test On Conflict") func onConflict() { check( SwifQL.on.conflict(CarBrands.column("id"), CarBrands.column("name")), .psql(#"ON CONFLICT ("id", "name")"#), .mysql("ON CONFLICT (id, name)") ) check( SwifQL.on.conflict(CarBrands.column("id"), CarBrands.column("name")).do.nothing, .psql(#"ON CONFLICT ("id", "name") DO NOTHING"#), .mysql("ON CONFLICT (id, name) DO NOTHING") ) } // MARK: - ON CONFLICT ON CONSTRAINT DO NOTHING @Test("Test On Conflict On Constraint Do Nothing") func onConflictOnConstraintDoNothing() { check( SwifQL.on.conflict.on.constraint("hello_world"), .psql(#"ON CONFLICT ON CONSTRAINT "hello_world""#), .mysql("ON CONFLICT ON CONSTRAINT hello_world") ) check( SwifQL.on.conflict.on.constraint("hello_world").do.nothing, .psql(#"ON CONFLICT ON CONSTRAINT "hello_world" DO NOTHING"#), .mysql("ON CONFLICT ON CONSTRAINT hello_world DO NOTHING") ) check( SwifQL.on.conflict.on.constraint(CarBrands.column("name")), .psql(#"ON CONFLICT ON CONSTRAINT "name""#), .mysql("ON CONFLICT ON CONSTRAINT name") ) } // MARK: - DO NOTHING @Test("Test Do Nothing") func doNothing() { check( SwifQL.do.nothing, .psql("DO NOTHING"), .mysql("DO NOTHING") ) } // MARK: - NOT / NOT BETWEEN / BETWEEN @Test("Test Not And Between") func notAndBetween() { check( SwifQL.between(10.and(20)), .psql("BETWEEN 10 AND 20"), .mysql("BETWEEN 10 AND 20") ) check( SwifQL.not(SwifQL.between(10.and(20))), .psql("NOT BETWEEN 10 AND 20"), .mysql("NOT BETWEEN 10 AND 20") ) check( SwifQL.between((CarBrands.column("id")).and(CarBrands.column("name"))), .psql(#"BETWEEN "CarBrands"."id" AND "CarBrands"."name""#), .mysql("BETWEEN CarBrands.id AND CarBrands.name") ) check( SwifQL.not(SwifQL.between((CarBrands.column("id")).and(CarBrands.column("name")))), .psql(#"NOT BETWEEN "CarBrands"."id" AND "CarBrands"."name""#), .mysql("NOT BETWEEN CarBrands.id AND CarBrands.name") ) } } ================================================ FILE: Tests/SwifQLTests/ExistsTests.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("Exists Tests") struct ExistsTests: SwifQLTests { //MARK: - EXISTS @Test("Test Exists") func exists() { check( SwifQL.exists(1), .psql("EXISTS (1)"), .mysql("EXISTS (1)") ) } //MARK: - NOT EXISTS @Test("Test Not Exists") func notExists() { check( SwifQL.notExists(1), .psql("NOT EXISTS (1)"), .mysql("NOT EXISTS (1)") ) } //MARK: - WHERE EXISTS @Test("Test Where Exists") func whereExists() { check( SwifQL.whereExists(1), .psql("WHERE EXISTS (1)"), .mysql("WHERE EXISTS (1)") ) } //MARK: - WHERE NOT EXISTS @Test("Test Where Not Exists") func whereNotExists() { check( SwifQL.whereNotExists(1), .psql("WHERE NOT EXISTS (1)"), .mysql("WHERE NOT EXISTS (1)") ) } } ================================================ FILE: Tests/SwifQLTests/FnTests.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("Fn Tests") struct FnTests: SwifQLTests { // MARK: - Fn.array_remove @Test("Test array_remove") func array_remove() { check( SwifQL.select(Fn.array_remove(PgArray(1,2,3,2), 2)), .psql("SELECT array_remove(ARRAY[1, 2, 3, 2], 2)"), .mysql("SELECT array_remove(ARRAY[1, 2, 3, 2], 2)") ) } // MARK: - Concat @Test("Test concat") func concat() { check( Fn.concat("Hello ", CarBrands.column("name")), .psql(#"concat('Hello ', "CarBrands"."name")"#), .mysql("concat('Hello ', CarBrands.name)") ) check( Fn.concat_ws(", ", "Hello", CarBrands.column("name")), .psql(#"concat_ws(', ', 'Hello', "CarBrands"."name")"#), .mysql("concat_ws(', ', 'Hello', CarBrands.name)") ) } // MARK: - Fn.to_tsvector @Test("Test to_tsvector") func to_tsvector() { check( SwifQL.select(Fn.to_tsvector("english", "a fat cat sat on a mat - it ate a fat rats")), .psql(#"SELECT to_tsvector('english', 'a fat cat sat on a mat - it ate a fat rats')"#), .mysql("SELECT to_tsvector('english', 'a fat cat sat on a mat - it ate a fat rats')") ) check( SwifQL.select(Fn.to_tsvector("english")), .psql("SELECT to_tsvector('english')"), .mysql("SELECT to_tsvector('english')") ) } // MARK: - Fn.to_tsquery @Test("Test to_tsquery") func to_tsquery() { check( SwifQL.select(Fn.to_tsquery("english", "The & Fat & Rats")), .psql("SELECT to_tsquery('english', 'The & Fat & Rats')"), .mysql("SELECT to_tsquery('english', 'The & Fat & Rats')") ) check( SwifQL.select(Fn.to_tsquery("english")), .psql("SELECT to_tsquery('english')"), .mysql("SELECT to_tsquery('english')") ) } // MARK: - Fn.plainto_tsquery @Test("Test plainto_tsquery") func plainto_tsquery() { check( SwifQL.select(Fn.plainto_tsquery("english", "The Fat Rats")), .psql("SELECT plainto_tsquery('english', 'The Fat Rats')"), .mysql("SELECT plainto_tsquery('english', 'The Fat Rats')") ) check( SwifQL.select(Fn.plainto_tsquery("english")), .psql("SELECT plainto_tsquery('english')"), .mysql("SELECT plainto_tsquery('english')") ) } // MARK: - Fn.ts_rank_cd @Test("Test ts_rank_cd") func ts_rank_cd() { check( SwifQL.select(Fn.ts_rank_cd(FormattedKeyPath(CarBrands.self, "id"), Fn.to_tsquery("The Fat Rats"))), .psql(#"SELECT ts_rank_cd("CarBrands"."id", to_tsquery('The Fat Rats'))"#), .mysql("SELECT ts_rank_cd(CarBrands.id, to_tsquery('The Fat Rats'))") ) } // MARK - Generate Series @Test("Test Generate Series Numbers") func generateSeriesNumbers() { check( SwifQL.select(Fn.generate_series(1, 4)), .psql("SELECT generate_series(1, 4)"), .mysql("SELECT generate_series(1, 4)") ) check( SwifQL.select(Fn.generate_series(1, 4, 2)), .psql("SELECT generate_series(1, 4, 2)"), .mysql("SELECT generate_series(1, 4, 2)") ) } @Test("Test Generate Series Dates") func generateSeriesDates() { check( SwifQL.select(Fn.generate_series("2019-10-01", "2019-10-04", "1 day")), .psql("SELECT generate_series('2019-10-01', '2019-10-04', '1 day')"), .mysql("SELECT generate_series('2019-10-01', '2019-10-04', '1 day')") ) check( SwifQL.select(Fn.generate_series("2019-10-01" => .date, "2019-10-04" => .date, "1 day")), .psql("SELECT generate_series('2019-10-01'::date, '2019-10-04'::date, '1 day')"), .mysql("SELECT generate_series('2019-10-01'::date, '2019-10-04'::date, '1 day')") ) let df = DateFormatter() df.dateFormat = "yyyy-MM-dd HH:mm:ss" df.timeZone = TimeZone(secondsFromGMT: 0) let pdf = PostgresDateFormatter() let date1 = df.date(from: "2019-10-01 00:00:00")! let date2 = df.date(from: "2019-10-04 00:00:00")! check( SwifQL.select(Fn.generate_series(date1, date2, "1 day")), .psql("SELECT generate_series(('\(pdf.string(from: date1))'::timestamptz), ('\(pdf.string(from: date2))'::timestamptz), '1 day')"), .mysql("SELECT generate_series(FROM_UNIXTIME(1569888000.0), FROM_UNIXTIME(1570147200.0), '1 day')") ) } // MARK: - MySQL DATE_FORMAT @Test("Test date_format") func date_format() { check( SwifQL.select(Fn.date_format(CarBrands.column("createdAt"), "%y-%m")), .psql(#"SELECT DATE_FORMAT("CarBrands"."createdAt", '%y-%m')"#), .mysql("SELECT DATE_FORMAT(CarBrands.createdAt, '%y-%m')") ) } } ================================================ FILE: Tests/SwifQLTests/FromTests.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("From Tests") struct FromTests: SwifQLTests { //MARK: - FROM @Test("Test Select From") func selectFrom() { check( SwifQL.select(1).from(), .psql("SELECT 1 FROM "), .mysql("SELECT 1 FROM ") ) } @Test("Test From") func from() { check( SwifQL.from(), .psql("FROM "), .mysql("FROM ") ) } @Test("Test From One Table") func fromOneTable() { check( SwifQL.from(CarBrands.table), .psql(#"FROM "CarBrands""#), .mysql("FROM CarBrands") ) } @Test("Test From Two Tables") func fromTwoTables() { check( SwifQL.from(CarBrands.table, CarBrands.table), .psql(#"FROM "CarBrands", "CarBrands""#), .mysql("FROM CarBrands, CarBrands") ) } @Test("Test From One Table Alias") func fromOneTableAlias() { check( SwifQL.from(cb.table), .psql(#"FROM "CarBrands" AS "cb""#), .mysql("FROM CarBrands AS cb") ) } @Test("Test From Two Table Aliases") func fromTwoTableAliases() { check( SwifQL.from(cb.table, cb.table), .psql(#"FROM "CarBrands" AS "cb", "CarBrands" AS "cb""#), .mysql("FROM CarBrands AS cb, CarBrands AS cb") ) } @Test("Test From Table And Table Alias") func fromTableAndTableAlias() { check( SwifQL.from(CarBrands.table, cb.table), .psql(#"FROM "CarBrands", "CarBrands" AS "cb""#), .mysql("FROM CarBrands, CarBrands AS cb") ) } } ================================================ FILE: Tests/SwifQLTests/JsonTests.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("Json Tests") struct JsonTests: SwifQLTests { // MARK: - JSON @Test("Test Json Extract Path") func jsonExtractPath() { let json = #"{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}"# check( SwifQL.select(Fn.json_extract_path(json, path_elems: ["f4"])), .psql(#"SELECT json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4')"#), .mysql(#"SELECT json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4')"#) ) } @Test("Test Json Extract Path Text") func jsonExtractPathText() { let json = #"{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}"# check( SwifQL.select(Fn.json_extract_path_text(json, path_elems: ["f4", "f6"])), .psql(#"SELECT json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4', 'f6')"#), .mysql(#"SELECT json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4', 'f6')"#) ) } @Test("Test Jsonb Extract Path") func jsonbExtractPath() { let json = #"{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}"# check( SwifQL.select(Fn.jsonb_extract_path(json, path_elems: ["f4"])), .psql(#"SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4')"#), .mysql(#"SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4')"#) ) } @Test("Test Jsonb Extract Path Text") func jsonbExtractPathText() { let json = #"{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}"# check( SwifQL.select(Fn.jsonb_extract_path_text(json, path_elems: ["f4", "f6"])), .psql(#"SELECT jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4', 'f6')"#), .mysql(#"SELECT jsonb_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4', 'f6')"#) ) } } ================================================ FILE: Tests/SwifQLTests/OrderTests.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("Order Tests") struct OrderTests: SwifQLTests { // MARK: - Order by simple @Test("Test Order By Simple") func orderBySimple() { check( SwifQL.orderBy(.asc(CarBrands.column("name")), .desc(CarBrands.column("id"))), .psql(#"ORDER BY "CarBrands"."name" ASC, "CarBrands"."id" DESC"#), .mysql("ORDER BY CarBrands.name ASC, CarBrands.id DESC") ) } // MARK: - Order by with nulls @Test("Test Order By With Nulls") func orderByWithNulls() { check( SwifQL.orderBy(.asc(CarBrands.column("name"), nulls: .first), .desc(CarBrands.column("id"), nulls: .last)), .psql(#"ORDER BY "CarBrands"."name" ASC NULLS FIRST, "CarBrands"."id" DESC NULLS LAST"#) ) check( SwifQL.orderBy(.asc(CarBrands.column("name") == nil, CarBrands.column("name")), .desc(CarBrands.column("id") != nil, CarBrands.column("id"))), .mysql("ORDER BY CarBrands.name IS NULL, CarBrands.name ASC, CarBrands.id IS NOT NULL, CarBrands.id DESC") ) } // MARK: - Order by with direction @Test("Test Order By Direction") func orderByDirection() { check( SwifQL.orderBy(.direction(.asc, CarBrands.column("name"), nulls: .last)), .psql(#"ORDER BY "CarBrands"."name" ASC NULLS LAST"#) ) check( SwifQL.orderBy(.direction(.desc, CarBrands.column("id"), nulls: .first)), .mysql("ORDER BY CarBrands.id DESC NULLS FIRST") ) } // MARK: - Order by random() / Order by rand() @Test("Test Order By Random") func orderByRandom() { check( SwifQL.orderBy(.random), .psql(#"ORDER BY random()"#) ) check( SwifQL.orderBy(.random), .mysql("ORDER BY rand()") ) } } ================================================ FILE: Tests/SwifQLTests/OtherTests.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("Other Tests") struct OtherTests: SwifQLTests { // MARK: String enum array @Test("Test String Enum Array") func stringEnumArray() { enum UserRole: String, SwifQLEnum { case admin case staff case vendor } check( SwifQL.in([UserRole.admin, .staff, .vendor]), .psql(#"IN ('admin', 'staff', 'vendor')"#), .mysql(#"IN ('admin', 'staff', 'vendor')"#) ) } // MARK: Int enum array @Test("Test Int Enum Array") func intEnumArray() { enum UserRole: Int, SwifQLEnum { case admin = 0 case staff = 1 case vendor = 2 } check( SwifQL.in([UserRole.admin, .staff, .vendor]), .psql(#"IN (0, 1, 2)"#), .mysql(#"IN (0, 1, 2)"#) ) } // MARK: Int enum value alone @Test("Test Int Enum Array With Alone Value") func intEnumValueAlone() { enum UserRole: Int, SwifQLEnum { case admin = 0 case staff = 1 case vendor = 2 } check( 0 == UserRole.admin, .psql(#"0 = 0"#), .mysql(#"0 = 0"#) ) } // MARK: String enum value alone @Test("Test String Enum Array With Value Alone") func stringEnumValueAlone() { enum UserRole: String, SwifQLEnum { case admin case staff case vendor } check( "admin" == UserRole.admin, .psql(#"'admin' = 'admin'"#), .mysql(#"'admin' = 'admin'"#) ) } // MARK: Null Condition @Test("Test Null Condition") func nullCondition() { check( "hello" == SwifQLNull, .psql(#"'hello' = NULL"#), .mysql(#"'hello' = NULL"#) ) check( "hello" == nil, .psql(#"'hello' IS NULL"#), .mysql(#"'hello' IS NULL"#) ) } // MARK: Enum array @Test("Test Enum Array") func enumArray() { check( [GearboxType.manual], .psql(#"'{manual}'"#), .mysql(#"'{manual}'"#) ) check( [GearboxType.manual, .auto], .psql(#"'{manual,auto}'"#), .mysql(#"'{manual,auto}'"#) ) } // MARK: Enum array type autodetect @Test("Test Array Type Autodetect") func enumArrayTypeAutodetect() { XCTAssertEqual(Type.auto(from: [GearboxType].self).name, "gearboxtype[]") } // MARK: ANY operator @Test("Test Operator") func anyOperator() { check( SwifQL.any("hello"), .psql(#"ANY('hello')"#), .mysql(#"ANY('hello')"#) ) } // MARK: Update @Test("Test Update With Schema") func updateWithSchema() { let alias = CarBrands.inSchema("hello") check( SwifQL.update(alias.table).set[items: alias.$id == 1, alias.$createdAt == 2], .psql(#"UPDATE "hello"."CarBrands" SET "id" = 1, "createdAt" = 2"#), .mysql(#"UPDATE hello.CarBrands SET id = 1, createdAt = 2"#) ) } @Test("Test Update Already Schemable With Different Schema") func updateAlreadySchemableWithDifferentSchema() { let alias = SchemableCarBrands.inSchema("hello") check( SwifQL.update(alias.table).set[items: alias.$id == 1, alias.$createdAt == 2], .psql(#"UPDATE "hello"."CarBrands" SET "id" = 1, "createdAt" = 2"#), .mysql(#"UPDATE hello.CarBrands SET id = 1, createdAt = 2"#) ) } // MARK: Alias @Test("Test Alias") func alias() { check( CarBrands.as("c"), .psql(#""c""#), .mysql(#"c"#) ) check( CarBrands.as("c").$id, .psql(#""c"."id""#), .mysql(#"c.id"#) ) check( CarBrands.as("c").table, .psql(#""CarBrands" AS "c""#), .mysql(#"CarBrands AS c"#) ) check( SchemableCarBrands.as("c").table, .psql(#""public"."CarBrands" AS "c""#), .mysql(#"public.CarBrands AS c"#) ) } // MARK: Create Type @Test("Test Create Type") func createType() { check( SwifQL.type("mood"), .psql(#"TYPE "mood""#), .mysql(#"TYPE mood"#) ) check( SwifQL.type("trololo", "mood"), .psql(#"TYPE "trololo"."mood""#), .mysql(#"TYPE trololo.mood"#) ) } // MARK: Select enum private enum Mood: String, SwifQLEnum { case sad, happy } @Test("Test Select Enum") func selectEnum() { check( SwifQL.select(Mood.happy), .psql(#"SELECT 'happy'"#), .mysql(#"SELECT 'happy'"#) ) } // MARK: Rename Table @Test("Test Rename Table") func renameTable() { check(UpdateTableBuilder().renameTable(to: "aaa"), .psql(#"ALTER TABLE "CarBrands" RENAME TO "aaa";"#)) } // MARK: Add Column @Test("Test Add Column") func addColumn() { check(UpdateTableBuilder().addColumn("aaa", .bigint, .default(0), .notNull), .psql(#"ALTER TABLE "CarBrands" ADD COLUMN "aaa" bigint DEFAULT 0 NOT NULL;"#)) } @Test("Test Add Column If Not Exits") func addColumnIfNotExits() { check(UpdateTableBuilder().addColumn("aaa", .bigint, .default(0), checkIfNotExists: true, .notNull), .psql(#"ALTER TABLE "CarBrands" ADD COLUMN IF NOT EXISTS "aaa" bigint DEFAULT 0 NOT NULL;"#)) } // MARK: Drop Column @Test("Test Drop Column") func dropColumn() { check(UpdateTableBuilder().dropColumn("aaa"), .psql(#"ALTER TABLE "CarBrands" DROP COLUMN "aaa";"#)) } @Test("Test Drop Column If Exists") func dropColumnIfExists() { check(UpdateTableBuilder().dropColumn("aaa", checkIfExists: true), .psql(#"ALTER TABLE "CarBrands" DROP COLUMN IF EXISTS "aaa";"#)) } @Test("Test Drop Column Cascade") func dropColumnCascade() { check(UpdateTableBuilder().dropColumn("aaa", cascade: true), .psql(#"ALTER TABLE "CarBrands" DROP COLUMN "aaa" CASCADE;"#)) } @Test("Test Drop Column If Exists Cascade") func dropColumnIfExistsCascade() { check(UpdateTableBuilder().dropColumn("aaa", checkIfExists: true, cascade: true), .psql(#"ALTER TABLE "CarBrands" DROP COLUMN IF EXISTS "aaa" CASCADE;"#)) } // MARK: Set Default @Test("Test Set Default") func setDefault() { check(UpdateTableBuilder().setDefault("aaa", constant: 0), .psql(#"ALTER TABLE "CarBrands" ALTER COLUMN "aaa" SET DEFAULT 0;"#)) } // MARK: Drop Default @Test("Test Drop Default") func dropDefault() { check(UpdateTableBuilder().dropDefault("aaa"), .psql(#"ALTER TABLE "CarBrands" ALTER COLUMN "aaa" DROP DEFAULT;"#)) } // MARK: Set Not Null @Test("Test Set Not Null") func setNotNull() { check(UpdateTableBuilder().setNotNull("aaa"), .psql(#"ALTER TABLE "CarBrands" ALTER COLUMN "aaa" SET NOT NULL;"#)) } // MARK: Drop Not Null @Test("Test Drop Not Null") func dropNotNull() { check(UpdateTableBuilder().dropNotNull("aaa"), .psql(#"ALTER TABLE "CarBrands" ALTER COLUMN "aaa" DROP NOT NULL;"#)) } // MARK: Rename Column @Test("Test Rename Column") func renameColumn() { check(UpdateTableBuilder().renameColumn("aaa", to: "bbb"), .psql(#"ALTER TABLE "CarBrands" RENAME COLUMN "aaa" TO "bbb";"#)) } // MARK: Add Unique @Test("Test Add Unique") func addUnique() { check(UpdateTableBuilder().addUnique(to: "aaa", "bbb"), .psql(#"ALTER TABLE "CarBrands" ADD UNIQUE ("aaa", "bbb");"#)) } // MARK: Add Primary Key @Test("Test Add Primary Key") func addPrimaryKey() { check(UpdateTableBuilder().addPrimaryKey(to: "aaa", "bbb"), .psql(#"ALTER TABLE "CarBrands" ADD PRIMARY KEY ("aaa", "bbb");"#)) } // MARK: Drop Constraint @Test("Test Drop Constraint") func dropConstraint() { check(UpdateTableBuilder().dropConstraint("aaa"), .psql(#"DROP CONSTRAINT "aaa";"#)) } // MARK: Drop Index @Test("Test Drop Index") func dropIndex() { check(UpdateTableBuilder().dropIndex(name: "aaa"), .psql(#"DROP INDEX "aaa";"#)) } // MARK: Create Index @Test("Test Create Index") func createIndex() { check( UpdateTableBuilder().createIndex( unique: true, name: "aaa", items: .column("column3", order: .desc), .expression(SwifQLBool(true) == SwifQLBool(true)), type: .hash, where: SwifQLBool(true) == SwifQLBool(true) ), .psql(#"CREATE UNIQUE INDEX "aaa" ON "CarBrands" USING HASH ("column3" DESC, (TRUE = TRUE)) WHERE TRUE = TRUE;"#) ) } // MARK: Add Check @Test("Test Add Check") func addCheck() { check(UpdateTableBuilder().addCheck(constraintName: "some_check", SwifQLBool(true) == SwifQLBool(false)), .psql(#"ALTER TABLE "CarBrands" ADD CONSTRAINT "some_check" CHECK (TRUE = FALSE);"#)) } // MARK: Add Foreign Key @Test("Test Add Foreign Key") func addForeignKey() { check(UpdateTableBuilder().addForeignKey(column: "aaa", constraintName: "fk_aaa", schema: "deleted", table: "User", columns: "id", onDelete: .cascade, onUpdate: .noAction), .psql(#"ALTER TABLE "CarBrands" ADD CONSTRAINT "fk_aaa" FOREIGN KEY ("aaa") REFERENCES "deleted"."User"("id") ON DELETE CASCADE ON UPDATE NO ACTION;"#)) } //MARK: - Operators @Test("Test Operator To SwifQLable") func operatorToSwifQLable() { check(SwifQL.select(Operator.null), .mysql("SELECT NULL"), .psql("SELECT NULL")) } //MARK: - ARRAY @Test("Test Array") func array() { let emptyIntArray: [Int] = [] check( emptyIntArray, // .mysql(""), .psql("") ) check( SwifQLableParts(parts: emptyIntArray), // .mysql("''"), .psql("'{}'") ) let nonEmptyIntArray: [Int] = [1,3,7] check( nonEmptyIntArray, // .mysql("1, 3, 7"), .psql("1, 3, 7") ) check( SwifQLableParts(parts: nonEmptyIntArray), // .mysql("'1,3,7'"), .psql("ARRAY[1,3,7]", "ARRAY[$1,$2,$3]") ) let emptyStringArray: [Int] = [] check( emptyStringArray, // .mysql(""), .psql("") ) check( SwifQLableParts(parts: emptyStringArray), // .mysql("''"), .psql("'{}'") ) let nonEmptyStringArray: [String] = ["a", "b", "c"] check( nonEmptyStringArray, // .mysql("'a', 'b', 'c'"), .psql("'a', 'b', 'c'") ) check( SwifQLableParts(parts: nonEmptyStringArray), // .mysql("'a','b','c'"), .psql("ARRAY['a','b','c']", "ARRAY[$1,$2,$3]") ) } //MARK: - WHERE @Test("Test Where") func `where`() { check(SwifQL.where("" == 1), all: "WHERE '' = 1") check(SwifQL.where, all: "WHERE") } //MARK: - UNION @Test("Test Union") func union() { let table1 = Path.Table("Table1") let table2 = Path.Table("Table2") let table3 = Path.Table("Table3") check( Union( SwifQL.select(table1.*).from(table1), SwifQL.select(table2.*).from(table2), SwifQL.select(table3.*).from(table3) ), .psql(#"(SELECT "Table1".* FROM "Table1") UNION (SELECT "Table2".* FROM "Table2") UNION (SELECT "Table3".* FROM "Table3")"#), .mysql(#"(SELECT Table1.* FROM Table1) UNION (SELECT Table2.* FROM Table2) UNION (SELECT Table3.* FROM Table3)"#) ) check( SwifQL .select(Distinct(Path.Column("uniqueName")) => .text => "name") .from( Union( SwifQL.select(Distinct(Path.Column("name")) => .text => "uniqueName").from(table1), SwifQL.select(Distinct(Path.Column("name")) => .text => "uniqueName").from(table2) ) ), .psql(#"SELECT DISTINCT "uniqueName"::text as "name" FROM (SELECT DISTINCT "name"::text as "uniqueName" FROM "Table1") UNION (SELECT DISTINCT "name"::text as "uniqueName" FROM "Table2")"#), .mysql(#"SELECT DISTINCT uniqueName::text as name FROM (SELECT DISTINCT name::text as uniqueName FROM Table1) UNION (SELECT DISTINCT name::text as uniqueName FROM Table2)"#) ) } //MARK: - VALUES @Test("Test Values") func values() { check( SwifQL.values(1, 1.2, 1.234, "hello"), .psql("(1, 1.2, 1.234, 'hello')"), .mysql("(1, 1.2, 1.234, 'hello')") ) check( SwifQL.values(array: [1, 1.2, 1.234, "hello"], [2, 2.3, 2.345, "bye"]), .psql("(1, 1.2, 1.234, 'hello'), (2, 2.3, 2.345, 'bye')"), .mysql("(1, 1.2, 1.234, 'hello'), (2, 2.3, 2.345, 'bye')") ) } // MARK: - BINDINGS @Test("Test Binding For PostgreSQL") func bindingForPostgreSQL() { let query = SwifQL.where(CarBrands.column("name") == "hello" || CarBrands.column("name") == "world").prepare(.psql).splitted.query XCTAssertEqual(query, """ WHERE "CarBrands"."name" = $1 OR "CarBrands"."name" = $2 """) } @Test("Test Binding For MySQL") func bindingForMySQL() { let query = SwifQL.where(CarBrands.column("name") == "hello" || CarBrands.column("name") == "world").prepare(.mysql).splitted.query XCTAssertEqual(query, """ WHERE CarBrands.name = ? OR CarBrands.name = ? """) } // MARK: - FormattedKeyPath @Test("Test Formatted KeyPath") func formattedKeyPath() { check( SwifQL.select(FormattedKeyPath(CarBrands.self, "id")), .psql(#"SELECT "CarBrands"."id""#), .mysql("SELECT CarBrands.id") ) check( SwifQL.select(CarBrands.mkp("id")), .psql(#"SELECT "CarBrands"."id""#), .mysql("SELECT CarBrands.id") ) } } ================================================ FILE: Tests/SwifQLTests/PredicateTest.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("Predicate Tests") struct PredicateTests: SwifQLTests { // MARK: - Greater Than (Number) @Test("Test Greater Than") func greaterThan() { check( SwifQL.where(\CarBrandReferences.$score > 10), .psql(#"WHERE "CarBrandReferences"."score" > 10"#), .mysql("WHERE CarBrandReferences.score > 10") ) } // MARK: - Less Than (Number) @Test("Test Less Than") func lessThan() { check( SwifQL.where(\CarBrandReferences.$score < 10), .psql(#"WHERE "CarBrandReferences"."score" < 10"#), .mysql("WHERE CarBrandReferences.score < 10") ) } // MARK: - Greater Than Or Equal (Number) @Test("Test Greater Than Or Equal") func greaterThanOrEqual() { check( SwifQL.where(\CarBrandReferences.$score >= 10), .psql(#"WHERE "CarBrandReferences"."score" >= 10"#), .mysql("WHERE CarBrandReferences.score >= 10") ) } // MARK: - Less Than Or Equal (Number) @Test("Test Less Than Or Equal") func lessThanOrEqual() { check( SwifQL.where(\CarBrandReferences.$score <= 10), .psql(#"WHERE "CarBrandReferences"."score" <= 10"#), .mysql("WHERE CarBrandReferences.score <= 10") ) } // MARK: - Equal (Number) @Test("Test Equal Number") func equalNumber() { check( SwifQL.where(\CarBrandReferences.$score == 10), .psql(#"WHERE "CarBrandReferences"."score" = 10"#), .mysql("WHERE CarBrandReferences.score = 10") ) } // MARK: - Equal (String) @Test("Test Equal String") func equalString() { check( SwifQL.where(\CarBrandReferences.$model == "x001"), .psql(#"WHERE "CarBrandReferences"."model" = 'x001'"#), .mysql("WHERE CarBrandReferences.model = 'x001'") ) } // MARK: - Equal (Boolean) @Test("Test Equal Boolean") func equalBoolean() { check( SwifQL.where(\CarBrandReferences.$available == true), .psql(#"WHERE "CarBrandReferences"."available" = TRUE"#), .mysql("WHERE CarBrandReferences.available = TRUE") ) } // MARK: - Equal (Enum) @Test("Test Equal A Type") func equalAType() { check( SwifQL.where(\CarBrandReferences.$mainGers == GearboxType.manual), .psql(#"WHERE "CarBrandReferences"."mainGers" = 'manual'"#), .mysql("WHERE CarBrandReferences.mainGers = 'manual'") ) } // MARK: - Equal (Null) @Test("Test Equal NULL") func equalNULL() { check( SwifQL.where(\CarBrandReferences.$model == nil), .psql(#"WHERE "CarBrandReferences"."model" IS NULL"#), .mysql("WHERE CarBrandReferences.model IS NULL") ) } // MARK: - Not Equal (Number) @Test("Test Not Equal Number") func notEqualNumber() { check( SwifQL.where(\CarBrandReferences.$score != 10), .psql(#"WHERE "CarBrandReferences"."score" != 10"#), .mysql("WHERE CarBrandReferences.score != 10") ) } // MARK: - Not Equal (String) @Test("Test Not Equal String") func notEqualString() { check( SwifQL.where(\CarBrandReferences.$model != "x001"), .psql(#"WHERE "CarBrandReferences"."model" != 'x001'"#), .mysql("WHERE CarBrandReferences.model != 'x001'") ) } // MARK: - Not Equal (Boolean) @Test("Test Not Equal Boolean") func notEqualBoolean() { check( SwifQL.where(\CarBrandReferences.$available != true), .psql(#"WHERE "CarBrandReferences"."available" != TRUE"#), .mysql("WHERE CarBrandReferences.available != TRUE") ) } // MARK: - Not Equal (Enum) @Test("Test Not Equal A Type") func notEqualAType() { check( SwifQL.where(\CarBrandReferences.$mainGers != GearboxType.manual), .psql(#"WHERE "CarBrandReferences"."mainGers" != 'manual'"#), .mysql("WHERE CarBrandReferences.mainGers != 'manual'") ) } // MARK: - Not Equal (Null) @Test("Test Not Equal NULL") func notEqualNULL() { check( SwifQL.where(\CarBrandReferences.$model != nil), .psql(#"WHERE "CarBrandReferences"."model" IS NOT NULL"#), .mysql("WHERE CarBrandReferences.model IS NOT NULL") ) } // MARK: - Between (Int) @Test("Test Between Int") func betweenInt () { check( SwifQL.where(\CarBrandReferences.$score <> 5 && 20), .psql(#"WHERE "CarBrandReferences"."score" BETWEEN 5 AND 20"#), .mysql("WHERE CarBrandReferences.score BETWEEN 5 AND 20") ) } // MARK: - Between (String) @Test("Test Between String") func betweenString () { check( SwifQL.where(\CarBrandReferences.$model <> "a" && "t"), .psql(#"WHERE "CarBrandReferences"."model" BETWEEN 'a' AND 't'"#), .mysql("WHERE CarBrandReferences.model BETWEEN 'a' AND 't'") ) } // MARK: - Range @> ([Enum]) @Test("Test Range Left Enum") func rangeLeftEnum() { check( SwifQL.where(\CarBrandReferences.$availableGers ||> [GearboxType.manual]), .psql(#"WHERE "CarBrandReferences"."availableGers" @> '{manual}'"#) ) } // MARK: - Range <@ ([Enum]) @Test("Test Range Right Enum") func rangeRightEnum() { check( SwifQL.where(\CarBrandReferences.$availableGers <|| [GearboxType.manual]), .psql(#"WHERE "CarBrandReferences"."availableGers" <@ '{manual}'"#) ) } // MARK: - Range @> ([String]) @Test("Test Range Left String") func rangeLeftString() { check( SwifQL.where(\CarBrandReferences.$tags ||> PgArray(["red"])), .psql(#"WHERE "CarBrandReferences"."tags" @> ARRAY['red']"#) ) } // MARK: - Range <@ ([String]) @Test("Test Range Right String") func rangeRightString() { check( SwifQL.where(\CarBrandReferences.$tags <|| PgArray(["red"])), .psql(#"WHERE "CarBrandReferences"."tags" <@ ARRAY['red']"#) ) } // MARK: - Range @> ([Int]) @Test("Test Range Left Int") func rangeLeftInt() { check( SwifQL.where(\CarBrandReferences.$availableYears ||> PgArray([1, 2, 3])), .psql(#"WHERE "CarBrandReferences"."availableYears" @> ARRAY[1, 2, 3]"#) ) } // MARK: - Range <@ ([Int]) @Test("Test Range Right Int") func rangeRightInt() { check( SwifQL.where(\CarBrandReferences.$availableYears <|| PgArray([1, 2, 3])), .psql(#"WHERE "CarBrandReferences"."availableYears" <@ ARRAY[1, 2, 3]"#) ) } // MARK: - Range @> ([UUID]) @Test("Test Range Left UUID") func rangeLeftUUID() { check( SwifQL.where(\CarBrandReferences.$knowedIssues ||> PgArray([UUID(uuidString: "25afebec-179e-43ff-8c7b-a0a460c7c996")!, UUID(uuidString: "25afebec-179e-43ff-8c7b-a0a460c7c996")!])), .psql(#"WHERE "CarBrandReferences"."knowedIssues" @> ARRAY['25AFEBEC-179E-43FF-8C7B-A0A460C7C996', '25AFEBEC-179E-43FF-8C7B-A0A460C7C996']"#) ) } // MARK: - Range <@ ([UUID]) @Test("Test Range Right UUID") func rangeRightUUID() { check( SwifQL.where(\CarBrandReferences.$knowedIssues <|| PgArray([UUID(uuidString: "25afebec-179e-43ff-8c7b-a0a460c7c996")!, UUID(uuidString: "25afebec-179e-43ff-8c7b-a0a460c7c996")!])), .psql(#"WHERE "CarBrandReferences"."knowedIssues" <@ ARRAY['25AFEBEC-179E-43FF-8C7B-A0A460C7C996', '25AFEBEC-179E-43FF-8C7B-A0A460C7C996']"#) ) } } ================================================ FILE: Tests/SwifQLTests/SelectTests.swift ================================================ @testable import SwifQL import Testing import Foundation @Suite("Select Tests") struct SelectTests: SwifQLTests { @Test("Test Select") func select() { check(SwifQL.select, all: "SELECT") check(SwifQL.select(), all: "SELECT ") } @Test("Test Select String") func selectString() { check( SwifQL.select("hello"), .psql("SELECT 'hello'"), .mysql("SELECT 'hello'") ) } // @Test("Test Select UUID Array") // func selectUUIDArray() { // check( // SwifQL ||> [PostgresArray(UUID(uuidString: "00000000-0000-0000-0000-000000000001"))], // .psql("SELECT 'hello'"), // .mysql("SELECT 'hello'") // ) // } @Test("Test Select Int") func selectInt() { check( SwifQL.select(1), .psql("SELECT 1"), .mysql("SELECT 1") ) } @Test("Test Select Double") func selectDouble() { check( SwifQL.select(1.234), .psql("SELECT 1.234"), .mysql("SELECT 1.234") ) } @Test("Test Select Date") func selectData() { let base64 = "Aa+/0w==" let data = Data(base64Encoded: base64)! check( SwifQL.select(data), .psql("SELECT decode('\(base64)', 'base64')") ) } @Test("Test Select Several Simple Values") func selectSeveralSimpleValues() { check( SwifQL.select("hello", 1, 1.234), .psql("SELECT 'hello', 1, 1.234"), .mysql("SELECT 'hello', 1, 1.234") ) } @Test("Test Select Column Path") func selectColumn_path() { check( SwifQL.select(Path.Column("id")), .psql(#"SELECT "id""#), .mysql("SELECT id") ) } @Test("Test Select Column KeyPath") func selectColumn_keyPath() { check( SwifQL.select(\CarBrands.$id), .psql(#"SELECT "CarBrands"."id""#), .mysql("SELECT CarBrands.id") ) } @Test("Test Select Column KeyPath Alias Simple") func selectColumn_keyPath_alias_simple() { check( SwifQL.select(\CarBrands.$id => "ident"), .psql(#"SELECT "CarBrands"."id" as "ident""#), .mysql("SELECT CarBrands.id as ident") ) } @Test("Test Select Column KeyPath Alias KeyPath") func selectColumn_keyPath_alias_keyPath() { struct Result: Aliasable { @Alias("ident") var abc: UUID init () {} } check( SwifQL.select(\CarBrands.$id => \Result.$abc), .psql(#"SELECT "CarBrands"."id" as "ident""#), .mysql("SELECT CarBrands.id as ident") ) } @Test("Test Select Column With Table") func selectColumnWithTable() { check( SwifQL.select(Path.Table("CarBrands").column("id")), .psql(#"SELECT "CarBrands"."id""#), .mysql("SELECT CarBrands.id") ) } @Test("Test Select Column With Table And Schema") func selectColumnWithTableAndSchema() { check( SwifQL.select(Path.Schema("test").table("CarBrands").column("id")), .psql(#"SELECT "test"."CarBrands"."id""#), .mysql("SELECT test.CarBrands.id") ) } @Test("Test Select Column Without Table") func selectColumnWithoutTable() { check( SwifQL.select(Path.Column("id")), .psql(#"SELECT "id""#), .mysql("SELECT id") ) } @Test("Test Generic Selector All") func genericSelector_all() { check( CarBrands.select, .psql(#"SELECT "CarBrands".* FROM "CarBrands""#), .mysql("SELECT CarBrands.* FROM CarBrands") ) } @Test("Select CarBrands") func selectCarBrands() { check( SwifQL.select(CarBrands.column("id")), .psql(#"SELECT "CarBrands"."id""#), .mysql("SELECT CarBrands.id") ) } @Test("Select Schemable CarBrands") func selectSchemableCarBrands() { check( SwifQL.select(SchemableCarBrands.column("id")), .psql(#"SELECT "public"."CarBrands"."id""#), .mysql("SELECT public.CarBrands.id") ) } @Test("Select CarBrands in Customer Schema") func selectCarBrandsInCustomSchema() { let cb = Schema("hello") check( SwifQL.select(cb.column("id")), .psql(#"SELECT "hello"."CarBrands"."id""#), .mysql("SELECT hello.CarBrands.id") ) } @Test("Select CarBrands Several Fields") func selectCarBrandsSeveralFields() { check( SwifQL.select(CarBrands.column("id"), CarBrands.column("name")), .psql(#"SELECT "CarBrands"."id", "CarBrands"."name""#), .mysql("SELECT CarBrands.id, CarBrands.name") ) } @Test("Select CarBrands With Alias") func selectCarBrandsWithAlias() { check( SwifQL.select(cb.column("id")), .psql(#"SELECT "cb"."id""#), .mysql("SELECT cb.id") ) } @Test("Select CarBrands With Alias Several Fields") func selectCarBrandsWithAliasSeveralFields() { check( SwifQL.select(cb.column("id"), cb.column("name")), .psql(#"SELECT "cb"."id", "cb"."name""#), .mysql("SELECT cb.id, cb.name") ) } @Test("Select CarBrands Several Fields Mixed") func selectCarBrandsSeveralFieldsMixed() { check( SwifQL.select(CarBrands.column("id"), cb.column("name"), CarBrands.column("createdAt")), .psql(#"SELECT "CarBrands"."id", "cb"."name", "CarBrands"."createdAt""#), .mysql("SELECT CarBrands.id, cb.name, CarBrands.createdAt") ) } //MARK: PostgreSQL Functions @Test("Select abs") func selectFnAbs() { check( SwifQL.select(Fn.abs(1)), .psql("SELECT abs(1)"), .mysql("SELECT abs(1)") ) } @Test("Select avg") func selectFnAvg() { check( SwifQL.select(Fn.avg(1)), .psql("SELECT avg(1)"), .mysql("SELECT avg(1)") ) } @Test("Select bit_length") func selectFnBitLength() { check( SwifQL.select(Fn.bit_length("hello")), .psql("SELECT bit_length('hello')"), .mysql("SELECT bit_length('hello')") ) } @Test("Select btrim") func selectFnBtrim() { check( SwifQL.select(Fn.btrim("hello", "ll")), .psql("SELECT btrim('hello', 'll')"), .mysql("SELECT btrim('hello', 'll')") ) } @Test("Select ceil") func selectFnCeil() { check( SwifQL.select(Fn.ceil(1.5)), .psql("SELECT ceil(1.5)"), .mysql("SELECT ceil(1.5)") ) } @Test("Select ceiling") func selectFnCeiling() { check( SwifQL.select(Fn.ceiling(1.5)), .psql("SELECT ceiling(1.5)"), .mysql("SELECT ceiling(1.5)") ) } @Test("Select char_length") func selectFnCharLength() { check( SwifQL.select(Fn.char_length("hello")), .psql("SELECT char_length('hello')"), .mysql("SELECT char_length('hello')") ) } @Test("Select character_length") func selectFnCharacter_length() { check( SwifQL.select(Fn.character_length("hello")), .psql("SELECT character_length('hello')"), .mysql("SELECT character_length('hello')") ) } @Test("Select initcap") func selectFnInitcap() { check( SwifQL.select(Fn.initcap("hello")), .psql("SELECT initcap('hello')"), .mysql("SELECT initcap('hello')") ) } @Test("Select length") func selectFnLength() { check( SwifQL.select(Fn.length("hello")), .psql("SELECT length('hello')"), .mysql("SELECT length('hello')") ) } @Test("Select lower") func selectFnLower() { check( SwifQL.select(Fn.lower("hello")), .psql("SELECT lower('hello')"), .mysql("SELECT lower('hello')") ) } @Test("Select lpad") func selectFnLpad() { check( SwifQL.select(Fn.lpad("hello", 3, "lo")), .psql("SELECT lpad('hello', 3, 'lo')"), .mysql("SELECT lpad('hello', 3, 'lo')") ) } @Test("Select ltrim") func selectFnLtrim() { check( SwifQL.select(Fn.ltrim("hello", "he")), .psql("SELECT ltrim('hello', 'he')"), .mysql("SELECT ltrim('hello', 'he')") ) } @Test("Select position") func selectFnPosition() { check( SwifQL.select(Fn.position("el", in: "hello")), .psql("SELECT position('el' IN 'hello')"), .mysql("SELECT position('el' IN 'hello')") ) } @Test("Select repeat") func selectFnRepeat() { check( SwifQL.select(Fn.repeat("hello", 2)), .psql("SELECT repeat('hello', 2)"), .mysql("SELECT repeat('hello', 2)") ) } @Test("Select replace") func selectFnReplace() { check( SwifQL.select(Fn.replace("hello", "el", "ol")), .psql("SELECT replace('hello', 'el', 'ol')"), .mysql("SELECT replace('hello', 'el', 'ol')") ) } @Test("Select rpad") func selectFnRpad() { check( SwifQL.select(Fn.rpad("hello", 2, "ho")), .psql("SELECT rpad('hello', 2, 'ho')"), .mysql("SELECT rpad('hello', 2, 'ho')") ) } @Test("Select rtrim") func selectFnRtrim() { check( SwifQL.select(Fn.rtrim(" hello ", " ")), .psql("SELECT rtrim(' hello ', ' ')"), .mysql("SELECT rtrim(' hello ', ' ')") ) } @Test("Select strpos") func selectFnStrpos() { check( SwifQL.select(Fn.strpos("hello", "ll")), .psql("SELECT strpos('hello', 'll')"), .mysql("SELECT strpos('hello', 'll')") ) } @Test("Select substring") func selectFnSubstring() { check( SwifQL.select(Fn.substring("hello", from: 1)), .psql("SELECT substring('hello' FROM 1)"), .mysql("SELECT substring('hello' FROM 1)") ) check( SwifQL.select(Fn.substring("hello", for: 4)), .psql("SELECT substring('hello' FOR 4)"), .mysql("SELECT substring('hello' FOR 4)") ) check( SwifQL.select(Fn.substring("hello", from: 1, for: 4)), .psql("SELECT substring('hello' FROM 1 FOR 4)"), .mysql("SELECT substring('hello' FROM 1 FOR 4)") ) } @Test("Select translate") func selectFnTranslate() { check( SwifQL.select(Fn.translate("hola", "hola", "hello")), .psql("SELECT translate('hola', 'hola', 'hello')"), .mysql("SELECT translate('hola', 'hola', 'hello')") ) } @Test("Select ltrim") func selectFnLTrim() { check( SwifQL.select(Fn.ltrim("hello", "he")), .psql("SELECT ltrim('hello', 'he')"), .mysql("SELECT ltrim('hello', 'he')") ) } @Test("Select upper") func selectFnUpper() { check( SwifQL.select(Fn.upper("hello")), .psql("SELECT upper('hello')"), .mysql("SELECT upper('hello')") ) } @Test("Select count") func selectFnCount() { check( SwifQL.select(Fn.count(CarBrands.column("id"))), .psql(#"SELECT count("CarBrands"."id")"#), .mysql("SELECT count(CarBrands.id)") ) check( SwifQL.select(Fn.count(cb.column("id"))), .psql(#"SELECT count("cb"."id")"#), .mysql("SELECT count(cb.id)") ) } @Test("Select div") func selectFnDiv() { check( SwifQL.select(Fn.div(12, 4)), .psql("SELECT div(12, 4)"), .mysql("SELECT div(12, 4)") ) } @Test("Select exp") func selectFnExp() { check( SwifQL.select(Fn.exp(12, 4)), .psql("SELECT exp(12, 4)"), .mysql("SELECT exp(12, 4)") ) } @Test("Select floor") func selectFnFloor() { check( SwifQL.select(Fn.floor(12)), .psql("SELECT floor(12)"), .mysql("SELECT floor(12)") ) } @Test("Select max") func selectFnMax() { check( SwifQL.select(Fn.max(CarBrands.column("id"))), .psql(#"SELECT max("CarBrands"."id")"#), .mysql("SELECT max(CarBrands.id)") ) check( SwifQL.select(Fn.max(cb.column("id"))), .psql(#"SELECT max("cb"."id")"#), .mysql("SELECT max(cb.id)") ) } @Test("Select min") func selectFnMin() { check( SwifQL.select(Fn.min(CarBrands.column("id"))), .psql(#"SELECT min("CarBrands"."id")"#), .mysql("SELECT min(CarBrands.id)") ) check( SwifQL.select(Fn.min(cb.column("id"))), .psql(#"SELECT min("cb"."id")"#), .mysql("SELECT min(cb.id)") ) } @Test("Select mod") func selectFnMod() { check( SwifQL.select(Fn.mod(12, 4)), .psql("SELECT mod(12, 4)"), .mysql("SELECT mod(12, 4)") ) } @Test("Select power") func selectFnPower() { check( SwifQL.select(Fn.power(12, 4)), .psql("SELECT power(12, 4)"), .mysql("SELECT power(12, 4)") ) } @Test("Select random") func selectFnRandom() { check( SwifQL.select(Fn.random()), .psql("SELECT random()"), .mysql("SELECT random()") ) } @Test("Select round") func selectFnRound() { check( SwifQL.select(Fn.round(12.43)), .psql("SELECT round(12.43)"), .mysql("SELECT round(12.43)") ) check( SwifQL.select(Fn.round(12.43, 1)), .psql("SELECT round(12.43, 1)"), .mysql("SELECT round(12.43, 1)") ) } @Test("Select setseed") func selectFnSetSeed() { check( SwifQL.select(Fn.setseed(12)), .psql("SELECT setseed(12)"), .mysql("SELECT setseed(12)") ) } @Test("Select sign") func selectFnSign() { check( SwifQL.select(Fn.sign(12)), .psql("SELECT sign(12)"), .mysql("SELECT sign(12)") ) } @Test("Select sqrt") func selectFnSqrt() { check( SwifQL.select(Fn.sqrt(16)), .psql("SELECT sqrt(16)"), .mysql("SELECT sqrt(16)") ) } @Test("Select sum") func selectFnSum() { check( SwifQL.select(Fn.sum(CarBrands.column("id"))), .psql(#"SELECT sum("CarBrands"."id")"#), .mysql("SELECT sum(CarBrands.id)") ) check( SwifQL.select(Fn.sum(cb.column("id"))), .psql(#"SELECT sum("cb"."id")"#), .mysql("SELECT sum(cb.id)") ) } @Test("Select string_agg") func selectFnStringAgg() { check( SwifQL.select(Fn.string_agg(CarBrands.column("name"), ", ")), .psql(#"SELECT string_agg("CarBrands"."name", ', ')"#), .mysql("SELECT string_agg(CarBrands.name, ', ')") ) } @Test("Select regexp_replace") func selectFnRegexpReplace() { check( SwifQL.select(Fn.regexp_replace("/full/path/to/filename", "^.+/", "")), .psql(#"SELECT regexp_replace('/full/path/to/filename', '^.+/', '')"#), .mysql("SELECT regexp_replace('/full/path/to/filename', '^.+/', '')") ) } // MARK: - SELECT with alias in select params @Test("Select With Alias In Select Params") func selectWithAliasInSelectParams() { check( SwifQL.select("hello", =>"aaa").from(|SwifQL.select(CarBrands.column("name")).from(CarBrands.table))| => "aaa", .psql(#"SELECT 'hello', "aaa" FROM (SELECT "CarBrands"."name" FROM "CarBrands") as "aaa""#), .mysql("SELECT 'hello', aaa FROM (SELECT CarBrands.name FROM CarBrands) as aaa") ) } } ================================================ FILE: Tests/SwifQLTests/SubqueryTests.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("Subquery Tests") struct SubqueryTests: SwifQLTests { // MARK: - SUBQUERY WITH ALIAS @Test("Test Subquery With Alias") func subqueryWithAlias() { let a = CarBrands.as("a") let b = CarBrands.as("b") // WRONG EXAMPLE because of `|` postfix operator near `alias1` // let query = SwifQL.select( // a~\.name, // |SwifQL.select(Fn.json_agg(=>"alias1") => "test1" ) // .from( // |SwifQL.select(b~\.name => "someName") // .from(b.table) // .where(b~\.id == a~\.id)| // ) => "alias1"| // ) // .from(a.table) // RIGHT EXAMPLE // so use subquery inside brackets or even better move it into variable (it'd be more beautiful and easy to support) check( SwifQL .select( a.column("name"), |(SwifQL.select(Fn.json_agg(=>"alias1") => "test1") .from( |SwifQL.select(b.column("name") => "someName") .from(b.table) .where(b.column("id") == a.column("id"))| ) => "alias1")| ) .from(a.table), .psql(#"SELECT "a"."name", (SELECT json_agg("alias1") as "test1" FROM (SELECT "b"."name" as "someName" FROM "CarBrands" AS "b" WHERE "b"."id" = "a"."id") as "alias1") FROM "CarBrands" AS "a""#), .mysql("SELECT a.name, (SELECT json_agg(alias1) as test1 FROM (SELECT b.name as someName FROM CarBrands AS b WHERE b.id = a.id) as alias1) FROM CarBrands AS a") ) } } ================================================ FILE: Tests/SwifQLTests/SwifQLTestCase.swift ================================================ import XCTest @testable import SwifQL protocol SwifQLTests {} extension SwifQLTests { func check(_ query: SwifQLable, _ dialects: QueryWithDialect...) { dialects.forEach { XCTAssertEqual(query.prepare($0.dialect).plain, $0.plainQuery) if let bindedQuery = $0.bindedQuery { let prepared = query.prepare($0.dialect) let splitted = prepared.splitted XCTAssertEqual(splitted.query, bindedQuery) } } } func check(_ query: SwifQLable, all rawQuery: String) { SQLDialect.all.forEach { XCTAssertEqual(query.prepare($0).plain, rawQuery) } } } struct DeletedSchema: Schemable { static var schemaName: String { "deleted" } } enum GearboxType: String, SwifQLEnum { case manual, auto, cvt } struct SchemableCarBrands: Table, Schemable { static var tableName: String { "CarBrands" } @Column("id") var id: UUID @Column("name") var name: String @Column("createdAt") var createdAt: Date init() {} } struct CarBrands: Table { @Column("id") var id: UUID @Column("name") var name: String @Column("createdAt") var createdAt: Date init() {} } struct CarBrandReferences: Table { @Column("id") var id: UUID @Column("model") var model: String @Column("available") var available: Bool @Column("score") var score: Int @Column("mainGers") var mainGers: GearboxType @Column("availableGers") var availableGers: [GearboxType] @Column("availableYears") var availableYears: [Int] @Column("knowedIssues") var knowedIssues: [UUID] @Column("tags") var tags: [String] init() {} } let cb = CarBrands.as("cb") struct QueryWithDialect { let dialect: SQLDialect let plainQuery: String let bindedQuery: String? static func psql(_ plainQuery: String, _ bindedQuery: String? = nil) -> Self { .init(dialect: .psql, plainQuery: plainQuery, bindedQuery: bindedQuery) } static func mysql(_ plainQuery: String, _ bindedQuery: String? = nil) -> Self { .init(dialect: .mysql, plainQuery: plainQuery, bindedQuery: bindedQuery) } } ================================================ FILE: Tests/SwifQLTests/TableEncoding.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("Table Encoding Tests") struct TableEncodingTests: SwifQLTests { @Test("Test Pure") func pure() { guard let encodedData = try? JSONEncoder().encode(PureTable()) else { XCTFail("Unable to encode PureTable") return } guard let _ = try? JSONDecoder().decode(PureStruct.self, from: encodedData) else { XCTFail("Unable to decode PureTable into PureStruct") return } } @Test("Test With Optional Column") func withOptionalColumn() { #if swift(>=5.4) XCTAssertNil(TableWithOptionalColumn().firstName) #endif } @Test("Test With KeyPaths") func withKeyPaths() { guard let encodedData = try? JSONEncoder().encode(KeyPathTable()) else { XCTFail("Unable to encode PureTable") return } guard let _ = try? JSONDecoder().decode(KeyPathStruct.self, from: encodedData) else { XCTFail("Unable to decode KeyPathTable into KeyPathStruct") return } } } fileprivate struct PureStruct: Codable { let id: UUID let first_name: String } fileprivate final class PureTable: Table { @Column("id") var id: UUID @Column("first_name") var firstName: String init () { id = UUID() firstName = "" } } fileprivate final class TableWithOptionalColumn: Table { @Column("first_name") var firstName: String? init () {} } fileprivate struct KeyPathStruct: Codable { let id: UUID let firstName: String } fileprivate final class KeyPathTable: Table, KeyPathEncodable { @Column("id") var id: UUID @Column("first_name") var firstName: String init () { id = UUID() firstName = "" } } ================================================ FILE: Tests/SwifQLTests/WithTests.swift ================================================ @testable import SwifQL import Testing import XCTest @Suite("With Tests") struct WithTests: SwifQLTests { @Test("Test With") func with() { check( SwifQL .with(.init(Path.Table("Table1"), SwifQL.select(Path.Table("Table2").*).from(Path.Table("Table2")))) .select(Path.Table("Table1").*) .from(Path.Table("Table1")), .psql(#"WITH "Table1" as (SELECT "Table2".* FROM "Table2") SELECT "Table1".* FROM "Table1""#), .mysql("WITH Table1 as (SELECT Table2.* FROM Table2) SELECT Table1.* FROM Table1") ) } @Test("Test With Columns") func withColumns() { struct Table1: Table { init() {} } check( SwifQL .with( .init(Table1.table, SwifQL.select(Table1.table.*).from(Table1.table)), .init(Path.Table("Table2"), columns: [Path.Column("hi"), Path.Column("there")], SwifQL.select(Path.Table("Table2").*).from(Path.Table("Table2"))) ) .select(Path.Table("Table3").*) .from(Path.Table("Table3")), .psql(#"WITH "Table1" as (SELECT "Table1".* FROM "Table1"), "Table2" ("hi", "there") as (SELECT "Table2".* FROM "Table2") SELECT "Table3".* FROM "Table3""#), .mysql("WITH Table1 as (SELECT Table1.* FROM Table1), Table2 (hi, there) as (SELECT Table2.* FROM Table2) SELECT Table3.* FROM Table3") ) } }