diff --git a/.github/workflows/dokka-gh-pages.yml b/.github/workflows/dokka-gh-pages.yml new file mode 100644 index 0000000..53c853c --- /dev/null +++ b/.github/workflows/dokka-gh-pages.yml @@ -0,0 +1,61 @@ +name: Deploy the API documentation to GitHub Pages with Dokka + +on: + push: + branches: [ "plugins-release" ] + pull_request: + branches: [ "plugins-release" ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Set up JDK 8 + uses: actions/setup-java@v4 + with: + java-version: "8" + distribution: "temurin" + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Build the distribution with Gradle Wrapper + run: ./gradlew :dokkaGeneratePublicationHtml + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: build/dokka/html/ + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/kotlin-jvm-ci.yml b/.github/workflows/kotlin-jvm-ci.yml new file mode 100644 index 0000000..f3135e8 --- /dev/null +++ b/.github/workflows/kotlin-jvm-ci.yml @@ -0,0 +1,45 @@ +name: CI + +on: + push: + branches: [ "*" ] +# pull_request: +# branches: [ "*" ] + +jobs: + check: + + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 8 + uses: actions/setup-java@v4 + with: + java-version: '8' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Check with Gradle Wrapper + run: ./gradlew check + + dependency-submission: + + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 8 + uses: actions/setup-java@v4 + with: + java-version: '8' + distribution: 'temurin' + + - name: Generate and submit dependency graph + uses: gradle/actions/dependency-submission@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5b06dda --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,47 @@ +# Change log + +## v0.5.0 / 2024-11-29 + +Because of [the Exposed SELECT DSL design changes](https://github.com/JetBrains/Exposed/pull/1916), and also because the old `DatabaseClient` creation APIs were poorly designed and too cumbersome, causing additional cognitive burdens on the users, this release has been completely overhauled. Some old APIs are removed directly because deprecating and delegating them to new ones fills the code base with unused code. Therefore, this release is **not source-compatible or binary-compatible** with v0.4.0. Please do not update unless you have time to adapt to the refactored changes. We are sorry for the inconvenience. From this version on, we will try to maintain source and binary compatibility, deprecating APIs instead of removing them in the foreseeable future. + +Please check out the [updated README](README.md) to upgrade to v0.5.0. + +Functional changes: + +* adapt to [the Exposed SELECT DSL design changes](https://github.com/JetBrains/Exposed/pull/1916) (resolve #8) +* rename the SQL DSL functions taking mapper parameters, adding "withMapper" prefixes (resolve #6) +* split the library into multiple modules including "core", "postgresql", "sql-dsl", and "sql-dsl-with-mapper" +* generalize the functions with the types `PgPool` and `PgConnection` to work with different RDBMSs, replacing them with `Pool` and `SqlConnection` +* get rid of all usages of `PgPool` which was deprecated in Vert.x 4.5 +* extract some new APIs and move some existing APIs into finer-grained subpackages, including `jdbc`, `exposed`, `vertx.sqlclient`, and `local` in the "core" module, and `postgresql` in the "postgresql" module +* overhaul the APIs related to the creation of Exposed `Database`s, Vert.x SQL clients, and `DatabaseClient`s + + * remove the `create...andSetRole` functions to create Vert.x SQL Clients, with their functionalities merged into the `create...` functions to create Vert.x SQL Clients + + * refactor the Exposed `Database` and Vert.x `SqlClient` creation APIs to be more configurable and straightforward + + * remove the extra shortcut `DatabaseClient` creation APIs such as `createPgPoolDatabaseClient` as they were poorly designed and too cumbersome, causing additional cognitive burdens on the users + + There are too many different combinations with different RDMBSs such as PostgreSQL and MySQL, and different Vert.x SQL Clients such as `SqlClient`, `Pool`, and `SqlConnection`. Therefore we don't provide such shortcut APIs anymore as they are just too cumbersome and cause additional cognitive burdens on the users, and we encourage the library users to create their own (see the updated guide in README.md for instructions). In this way, the Exposed `Databse`s and Vert.x SQL Clients are also more configurable. + +* adopt `EvscConfig` as the single-source-of-truth database client config and no longer prefer local connections + + `LocalConnectionConfig` should be converted to `EvscConfig` or `ConnectionConfig` to be used. + +* mark some APIs as experimental as they are subject to change +* make `DatabaseClient` implement our new `CoroutineAutoCloseable` interface +* add a version of `selectWithMapper` without `buildQuery` +* point out in the usage guide that you are encouraged to share/reuse an Exposed `Database` which generates SQLs among multiple `DatabaseClient`s in multiple verticles, which improves performance as shown in our benchmark results + +Miscellaneous changes: + +* add API documentation generated by Dokka hosted at +* add CODE_OF_CONDUCT.md and CONTRIBUTING.md +* use the Kotlin binary compatibility validator +* bump Exposed to v0.56.0 + +## v0.4.0 / 2024-10-19 + +* bump Exposed to 0.53.0 +* fix a bug that an Exposed transaction is required if a query `FieldSet` contains custom functions depending on dialects and no such a transaction is provided +* Add a basic usage guide diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..da80987 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +shreckye@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ca714df --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# Contributing guidelines + +Hello, thank you for your interest in contributing to our project. + +## Issues and Discussions + +You are welcome to submit issues on bugs or feature requests. If you have questions, please ask them in GitHub Discussions. + +## Pull requests + +If you want to contribute to the code of our project, you are welcome to open pull requests. However, it's always a good idea to open a related issue or talk with us in Discussions first. + +## Development + +Please make sure you have a valid JDK installed. Some projects may require multiple JDKs of different versions. The JDK version we use can be found in the [GitHub Actions workflow files](.github/workflows). + +We recommend developing with IntelliJ IDEA. In IntelliJ IDEA, select the correct [Project SDK in Project Structure](https://www.jetbrains.com/help/idea/project-settings-and-structure.html#project-sdk) and it's recommended to set [Gradle JVM](https://www.jetbrains.com/help/idea/gradle-jvm-selection.html#jvm_settings) to "Project SDK". + +Run the `publishToMavenLocal` Gradle task to publish the libraries to your machine's Maven Local Repository so your projects can depend on the changes you have made, run `check` to ensure our limited number of tests pass. + +## Furthur notice + +We are currently a small team with limited effort. While we may not always implement your requested features, merge your pull requests, or do such things in time, you are always welcome to create your own fork and make any changes you like. diff --git a/README.md b/README.md index 3282176..a7d6a14 100644 --- a/README.md +++ b/README.md @@ -8,25 +8,89 @@ Only PostgreSQL with [Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) is currently supported. -## Maven coordinate +## Experimental + +This library is experimental now. The APIs are subject to change (especially those marked with `@ExperimentalEvscApi`), the tests are incomplete, and please expect bugs and report them. + +## Add to your dependencies + +### The Maven coordinates ```kotlin -"com.huanshankeji:exposed-vertx-sql-client-postgresql:$version" +"com.huanshankeji:exposed-vertx-sql-client-$module:$libraryVersion" ``` +### **Important note** + +As Exposed is a library that has not reached stability yet and often has incompatible changes, you are recommended to stick to the same version of Exposed used by this library. The current version is v0.56.0. + +## API documentation + +See the [hosted API documentation](https://huanshankeji.github.io/exposed-vertx-sql-client/) for the APIs. + ## Basic usage guide -Here is a basic usage guide. This project currently serves our own use, therefore, there are temporarily no detailed docs, APIs are experimental, tests are incomplete, and please expect bugs. To learn more in addition to the guide below, see [DatabaseClient.kt](lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/DatabaseClient.kt) and [DatabaseClientSql.kt](lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/DatabaseClientSql.kt) for the major APIs. +Here is a basic usage guide. + +### Before v0.5.0 + +Add the PostgreSQL module, which was the only module, to your dependencies with the Gradle build script: + +```kotlin +implementation("com.huanshankeji:exposed-vertx-sql-client-postgresql:$libraryVersion") +``` + +### Since v0.5.0 + +Add the core module to your dependencies with the Gradle build script: + +```kotlin +implementation("com.huanshankeji:exposed-vertx-sql-client-core:$libraryVersion") +``` + +And add an RDBMS module, for example, the PostgreSQL module: + +```kotlin +implementation("com.huanshankeji:exposed-vertx-sql-client-postgresql:$libraryVersion") +``` ### Create a `DatabaseClient` +Create an `EvscConfig` as the single source of truth: + +```kotlin +val evscConfig = ConnectionConfig.Socket("localhost", user = "user", password = "password", database = "database") + .toUniversalEvscConfig() +``` + +Local alternative with Unix domain socket: + +```kotlin +val evscConfig = defaultPostgresqlLocalConnectionConfig( + user = "user", + socketConnectionPassword = "password", + database = "database" +).toPerformantUnixEvscConfig() +``` + +Create an Exposed `Database` with the `ConnectionConfig.Socket`, which can be shared and reused in multiple `Verticle`s: + +```kotlin +val exposedDatabase = evscConfig.exposedConnectionConfig.exposedDatabaseConnectPostgresql() +``` + +Create a Vert.x `SqlClient` with the `ConnectionConfig`, preferably in a `Verticle`: + +```kotlin +val sqlClient = createPgClient(vertx, evscConfig.vertxSqlClientConnectionConfig) +val pool = createPgPool(vertx, evscConfig.vertxSqlClientConnectionConfig) +val sqlConnection = createPgClient(vertx, evscConfig.vertxSqlClientConnectionConfig) +``` + +Create a `Database` with the provided Vert.x `SqlClient` and Exposed `Database`, preferably in a `Verticle`: + ```kotlin -val socketConnectionConfig = - ConnectionConfig.Socket("localhost", user = "user", password = "password", database = "database") -val exposedDatabase = exposedDatabaseConnectPostgreSql(socketConnectionConfig) -val databaseClient = createPgPoolDatabaseClient( - vertx, socketConnectionConfig, exposedDatabase = exposedDatabase -) +val databaseClient = DatabaseClient(vertxSqlClient, exposedDatabase) ``` ### Example table definitions @@ -44,18 +108,19 @@ val tables = arrayOf(Examples) For example, to create tables: ```kotlin -withContext(Dispatchers.IO) { - databaseClient.exposedTransaction { - SchemaUtils.create(*tables) - } +databaseClient.exposedTransaction { + SchemaUtils.create(*tables) } ``` +If you execute blocking Exposed statements inside `Verticle`s or event loop threads that you shouldn't block, you should use Vert.x `Vertx.executeBlocking` or Coroutines `Dispatchers.IO`. + ### CRUD (DML and DQL) operations with `DatabaseClient` #### Core APIs -With these core APIs, you create and execute Exposed `Statement`s. You don't need to learn many new APIs, and the `Statement`s are more composable and easily editable. +With these core APIs, you create and execute Exposed `Statement`s. You don't need to learn many new APIs, and the +`Statement`s are more composable and easily editable. For example, you can move a query into an adapted subquery. ```kotlin // The Exposed `Table` extension functions `insert`, `update`, and `delete` execute eagerly so `insertStatement`, `updateStatement`, `deleteStatement` have to be used. @@ -72,17 +137,25 @@ val updateRowCount = databaseClient.executeUpdate(Examples.updateStatement({ Examples.id eq 1 }) { it[name] = "AA" }) assert(updateRowCount == 1) -// The Exposed `Table` extension function `select` doesn't execute eagerly so it can be used directly. -val exampleName = databaseClient.executeQuery(Examples.select(Examples.name).where(Examples.id eq 1)) +// The Exposed `Table` extension function `select` doesn't execute eagerly so it can also be used directly. +val exampleName = databaseClient.executeQuery(Examples.selectStatement(Examples.name).where(Examples.id eq 1)) .single()[Examples.name] -databaseClient.executeSingleUpdate(Examples.deleteWhereStatement { Examples.id eq 1 }) // The function `deleteWhereStatement` still depends on the old DSL and will be updated. +databaseClient.executeSingleUpdate(Examples.deleteWhereStatement { id eq 1 }) databaseClient.executeSingleUpdate(Examples.deleteIgnoreWhereStatement { id eq 2 }) ``` #### Extension SQL DSL APIs -With these extension APIs, your code becomes more concise, but it might be more difficult when you need to compose statements or edit the code: +With the extension SQL DSL APIs, your code becomes more concise, but it might be more difficult when you need to compose statements or edit the code. + +Gradle dependency configuration (only needed since v0.5.0): + +```kotlin +implementation("com.huanshankeji:exposed-vertx-sql-client-sql-dsl:$libraryVersion") +``` + +Example code: ```kotlin databaseClient.insert(Examples) { it[name] = "A" } @@ -92,9 +165,10 @@ val updateRowCount = databaseClient.update(Examples, { Examples.id eq 1 }) { it[ val exampleName1 = databaseClient.select(Examples) { select(Examples.name).where(Examples.id eq 1) }.single()[Examples.name] -// This function still depends on the old SELECT DSL and will be updated. val exampleName2 = - databaseClient.selectSingleColumn(Examples, Examples.name) { selectAll().where(Examples.id eq 2) }.single() + databaseClient.selectSingleColumn(Examples, Examples.name) { where(Examples.id eq 2) }.single() + +val examplesExist = databaseClient.selectExpression(exists(Examples.selectAll())) val deleteRowCount1 = databaseClient.deleteWhere(Examples) { id eq 1 } assert(deleteRowCount1 == 1) @@ -102,26 +176,34 @@ val deleteRowCount2 = databaseClient.deleteIgnoreWhere(Examples) { id eq 2 } assert(deleteRowCount2 == 1) ``` -#### APIs using [Exposed GADT mapping](https://github.com/huanshankeji/exposed-adt-mapping) +#### Extension SQL DSL APIs with [Exposed GADT mapping](https://github.com/huanshankeji/exposed-adt-mapping) Please read [that library's basic usage guide](https://github.com/huanshankeji/exposed-adt-mapping?tab=readme-ov-file#basic-usage-guide) first. Here are examples of this library that correspond to [that library's CRUD operations](https://github.com/huanshankeji/exposed-adt-mapping?tab=readme-ov-file#crud-operations). +Gradle dependency configuration (only needed since v0.5.0): + +```kotlin +implementation("com.huanshankeji:exposed-vertx-sql-client-sql-dsl-with-mapper:$libraryVersion") +``` + +Example code: + ```kotlin val directorId = 1 val director = Director(directorId, "George Lucas") -databaseClient.insert(Directors, director, Mappers.director) +databaseClient.insertWithMapper(Directors, director, Mappers.director) val episodeIFilmDetails = FilmDetails(1, "Star Wars: Episode I – The Phantom Menace", directorId) // insert without the ID since it's `AUTO_INCREMENT` -databaseClient.insert(Films, episodeIFilmDetails, Mappers.filmDetailsWithDirectorId) +databaseClient.insertWithMapper(Films, episodeIFilmDetails, Mappers.filmDetailsWithDirectorId) val filmId = 2 val episodeIIFilmDetails = FilmDetails(2, "Star Wars: Episode II – Attack of the Clones", directorId) val filmWithDirectorId = FilmWithDirectorId(filmId, episodeIIFilmDetails) -databaseClient.insert(Films, filmWithDirectorId, Mappers.filmWithDirectorId) // insert with the ID +databaseClient.insertWithMapper(Films, filmWithDirectorId, Mappers.filmWithDirectorId) // insert with the ID -val fullFilms = databaseClient.select(filmsLeftJoinDirectors, Mappers.fullFilm) { - select(Films.filmId inList listOf(1, 2)) // This API still depends on the old SELECT DSL and will be refactored. +val fullFilms = databaseClient.selectWithMapper(filmsLeftJoinDirectors, Mappers.fullFilm) { + where(Films.filmId inList listOf(1, 2)) } ``` diff --git a/build.gradle.kts b/build.gradle.kts index 1241019..35587e3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,12 @@ tasks.wrapper { distributionType = Wrapper.DistributionType.ALL } + +plugins { + id("org.jetbrains.dokka") + id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.3" +} + +dependencies { + dokka(project(":exposed-vertx-sql-client-postgresql")) +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index bf8774c..cab0d18 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -18,5 +18,6 @@ dependencies { // With Kotlin 2.0.20, a "Could not parse POM" build error occurs in the JVM projects of some dependent projects. implementation(kotlin("gradle-plugin", "2.0.10")) implementation("com.huanshankeji:common-gradle-dependencies:0.8.0-20241016") // don't use a snapshot version in a main branch - implementation("com.huanshankeji.team:gradle-plugins:0.6.0") // don't use a snapshot version in a main branch + implementation("com.huanshankeji.team:gradle-plugins:0.7.0") // don't use a snapshot version in a main branch + implementation("org.jetbrains.dokka:dokka-gradle-plugin:2.0.0-Beta") } diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 4453aa3..7082144 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -2,13 +2,13 @@ import com.huanshankeji.CommonDependencies import com.huanshankeji.CommonGradleClasspathDependencies import com.huanshankeji.CommonVersions -val projectVersion = "0.4.0" +val projectVersion = "0.5.0" // don't use a snapshot version in a main branch -val commonVersions = CommonVersions(kotlinCommon = "0.5.1") +val commonVersions = CommonVersions(kotlinCommon = "0.6.0", exposed = "0.56.0") val commonDependencies = CommonDependencies(commonVersions) val commonGradleClasspathDependencies = CommonGradleClasspathDependencies(commonVersions) object DependencyVersions { - val exposedAdtMapping = "0.2.0" + val exposedAdtMapping = "0.3.0" // don't use a snapshot version in a main branch } diff --git a/buildSrc/src/main/kotlin/conventions.gradle.kts b/buildSrc/src/main/kotlin/conventions.gradle.kts index adb9b61..922292b 100644 --- a/buildSrc/src/main/kotlin/conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/conventions.gradle.kts @@ -1,12 +1,9 @@ -import com.huanshankeji.team.`Shreck Ye` -import com.huanshankeji.team.pomForTeamDefaultOpenSource import com.huanshankeji.team.repositoriesAddTeamGithubPackagesMavenRegistry -import org.gradle.kotlin.dsl.repositories +import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask plugins { id("com.huanshankeji.team.with-group") - id("com.huanshankeji.kotlin-jvm-library-sonatype-ossrh-publish-conventions") - id("com.huanshankeji.team.default-github-packages-maven-publish") + kotlin("jvm") } repositories { @@ -19,8 +16,6 @@ kotlin.jvmToolchain(8) version = projectVersion -publishing.publications.getByName("maven") { - pomForTeamDefaultOpenSource(project, "Exposed Vert.x SQL Client", "Exposed on top of Vert.x Reactive SQL Client") { - `Shreck Ye`() - } +tasks.named>("compileKotlin").configure { + compilerOptions.freeCompilerArgs.add("-opt-in=com.huanshankeji.exposedvertxsqlclient.InternalApi") } diff --git a/buildSrc/src/main/kotlin/lib-conventions.gradle.kts b/buildSrc/src/main/kotlin/lib-conventions.gradle.kts new file mode 100644 index 0000000..e679e3d --- /dev/null +++ b/buildSrc/src/main/kotlin/lib-conventions.gradle.kts @@ -0,0 +1,15 @@ +import com.huanshankeji.team.`Shreck Ye` +import com.huanshankeji.team.pomForTeamDefaultOpenSource + +plugins { + id("conventions") + id("com.huanshankeji.kotlin-jvm-library-sonatype-ossrh-publish-conventions") + id("com.huanshankeji.team.default-github-packages-maven-publish") + id("com.huanshankeji.team.dokka.github-dokka-convention") +} + +publishing.publications.getByName("maven") { + pomForTeamDefaultOpenSource(project, "Exposed Vert.x SQL Client", "Exposed on top of Vert.x Reactive SQL Client") { + `Shreck Ye`() + } +} diff --git a/core/api/exposed-vertx-sql-client-core.api b/core/api/exposed-vertx-sql-client-core.api new file mode 100644 index 0000000..fe39f10 --- /dev/null +++ b/core/api/exposed-vertx-sql-client-core.api @@ -0,0 +1,172 @@ +public abstract interface class com/huanshankeji/exposedvertxsqlclient/ConnectionConfig { + public abstract fun getDatabase ()Ljava/lang/String; + public abstract fun getUserAndRole ()Ljava/lang/String; +} + +public final class com/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket : com/huanshankeji/exposedvertxsqlclient/ConnectionConfig { + public fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getDatabase ()Ljava/lang/String; + public final fun getHost ()Ljava/lang/String; + public final fun getPassword ()Ljava/lang/String; + public final fun getPort ()Ljava/lang/Integer; + public final fun getUser ()Ljava/lang/String; + public fun getUserAndRole ()Ljava/lang/String; +} + +public final class com/huanshankeji/exposedvertxsqlclient/ConnectionConfig$UnixDomainSocketWithPeerAuthentication : com/huanshankeji/exposedvertxsqlclient/ConnectionConfig { + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public fun getDatabase ()Ljava/lang/String; + public final fun getPath ()Ljava/lang/String; + public final fun getRole ()Ljava/lang/String; + public fun getUserAndRole ()Ljava/lang/String; +} + +public final class com/huanshankeji/exposedvertxsqlclient/ConnectionConfigKt { + public static final fun toUniversalEvscConfig (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;)Lcom/huanshankeji/exposedvertxsqlclient/EvscConfig; +} + +public final class com/huanshankeji/exposedvertxsqlclient/ConnectionType : java/lang/Enum { + public static final field Socket Lcom/huanshankeji/exposedvertxsqlclient/ConnectionType; + public static final field UnixDomainSocketWithPeerAuthentication Lcom/huanshankeji/exposedvertxsqlclient/ConnectionType; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/huanshankeji/exposedvertxsqlclient/ConnectionType; + public static fun values ()[Lcom/huanshankeji/exposedvertxsqlclient/ConnectionType; +} + +public final class com/huanshankeji/exposedvertxsqlclient/DatabaseClient : com/huanshankeji/kotlinx/coroutine/CoroutineAutoCloseable { + public fun (Lio/vertx/sqlclient/SqlClient;Lorg/jetbrains/exposed/sql/Database;ZZ)V + public synthetic fun (Lio/vertx/sqlclient/SqlClient;Lorg/jetbrains/exposed/sql/Database;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun createTable (Lorg/jetbrains/exposed/sql/Table;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun doExecute (Lorg/jetbrains/exposed/sql/statements/Statement;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun dropTable (Lorg/jetbrains/exposed/sql/Table;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun execute (Lorg/jetbrains/exposed/sql/statements/Statement;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeBatch (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeBatchForVertxSqlClientRowSetSequence (Ljava/lang/Iterable;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeBatchQuery (Lorg/jetbrains/exposed/sql/FieldSet;Ljava/lang/Iterable;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeBatchQuery (Lorg/jetbrains/exposed/sql/FieldSet;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeBatchUpdate (Ljava/lang/Iterable;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeExpression (Lkotlin/reflect/KClass;Lorg/jetbrains/exposed/sql/Expression;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeForVertxSqlClientRowSet (Lorg/jetbrains/exposed/sql/statements/Statement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executePlainSql (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executePlainSqlUpdate (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeQuery (Lorg/jetbrains/exposed/sql/Query;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeQuery (Lorg/jetbrains/exposed/sql/Query;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeSingleOrNoUpdate (Lorg/jetbrains/exposed/sql/statements/Statement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeSingleUpdate (Lorg/jetbrains/exposed/sql/statements/Statement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeUpdate (Lorg/jetbrains/exposed/sql/statements/Statement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun executeWithMapping (Lorg/jetbrains/exposed/sql/statements/Statement;Ljava/util/function/Function;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun exposedTransaction (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public final fun getExposedDatabase ()Lorg/jetbrains/exposed/sql/Database; + public final fun getFieldExpressionSetWithTransaction (Lorg/jetbrains/exposed/sql/FieldSet;)Ljava/util/Set; + public final fun getFieldExpressionSetWithTransaction (Lorg/jetbrains/exposed/sql/Query;)Ljava/util/Set; + public final fun getLogSql ()Z + public final fun getValidateBatch ()Z + public final fun getVertxSqlClient ()Lio/vertx/sqlclient/SqlClient; + public final fun isWorking (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun toExposedResultRowWithTransaction (Lio/vertx/sqlclient/Row;Lorg/jetbrains/exposed/sql/Query;)Lorg/jetbrains/exposed/sql/ResultRow; +} + +public final class com/huanshankeji/exposedvertxsqlclient/DatabaseClientKt { + public static final fun dbAssert (Z)V + public static final fun getFieldExpressionSet (Lorg/jetbrains/exposed/sql/FieldSet;)Ljava/util/Set; + public static final fun getFieldExpressionSet (Lorg/jetbrains/exposed/sql/Query;)Ljava/util/Set; + public static final fun getSavepointNameRegex ()Lkotlin/text/Regex; + public static final fun getVertxPgClientPreparedSql (Lorg/jetbrains/exposed/sql/statements/Statement;Lorg/jetbrains/exposed/sql/Transaction;)Ljava/lang/String; + public static final fun getVertxSqlClientArgTuple (Lorg/jetbrains/exposed/sql/statements/Statement;)Lio/vertx/sqlclient/Tuple; + public static final fun singleOrNoResult (Lio/vertx/sqlclient/RowSet;)Ljava/lang/Object; + public static final fun singleOrNoUpdate (I)Z + public static final fun singleResult (Lio/vertx/sqlclient/RowSet;)Ljava/lang/Object; + public static final fun singleStatementArguments (Lorg/jetbrains/exposed/sql/statements/Statement;)Ljava/lang/Iterable; + public static final fun toExposedResultRow (Lio/vertx/sqlclient/Row;Ljava/util/Set;)Lorg/jetbrains/exposed/sql/ResultRow; + public static final fun toExposedResultRow (Lio/vertx/sqlclient/Row;Lorg/jetbrains/exposed/sql/Query;)Lorg/jetbrains/exposed/sql/ResultRow; + public static final fun toVertxPgClientPreparedSql (Ljava/lang/String;)Ljava/lang/String; + public static final fun toVertxTuple (Ljava/lang/Iterable;)Lio/vertx/sqlclient/Tuple; + public static final fun types (Ljava/lang/Iterable;)Ljava/util/List; + public static final fun withSavepointAndRollbackIfThrows (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun withSavepointAndRollbackIfThrowsOrFalse (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun withSavepointAndRollbackIfThrowsOrLeft (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun withSavepointAndRollbackIfThrowsOrNone (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun withTransaction (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun withTransactionCommitOrRollback (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun withTypedTransaction (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class com/huanshankeji/exposedvertxsqlclient/EvscConfig : com/huanshankeji/exposedvertxsqlclient/IEvscConfig { + public fun (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;)V + public fun getExposedConnectionConfig ()Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket; + public fun getVertxSqlClientConnectionConfig ()Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig; +} + +public abstract interface annotation class com/huanshankeji/exposedvertxsqlclient/ExperimentalEvscApi : java/lang/annotation/Annotation { +} + +public abstract interface class com/huanshankeji/exposedvertxsqlclient/IEvscConfig { + public abstract fun getExposedConnectionConfig ()Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket; + public abstract fun getVertxSqlClientConnectionConfig ()Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig; +} + +public abstract interface annotation class com/huanshankeji/exposedvertxsqlclient/InternalApi : java/lang/annotation/Annotation { +} + +public final class com/huanshankeji/exposedvertxsqlclient/SingleUpdateException : java/lang/Exception { + public fun (I)V +} + +public final class com/huanshankeji/exposedvertxsqlclient/exposed/ExposedDatabasesKt { + public static final fun exposedDatabaseConnect (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; + public static final fun exposedDatabaseConnect (Ljava/lang/String;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; + public static synthetic fun exposedDatabaseConnect$default (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; + public static synthetic fun exposedDatabaseConnect$default (Ljava/lang/String;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; +} + +public final class com/huanshankeji/exposedvertxsqlclient/jdbc/JdbcUrlsKt { + public static final fun jdbcUrl (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;Ljava/lang/String;)Ljava/lang/String; +} + +public final class com/huanshankeji/exposedvertxsqlclient/local/LocalConnectionConfig { + public static final field Companion Lcom/huanshankeji/exposedvertxsqlclient/local/LocalConnectionConfig$Companion; + public static final field SOCKET_HOST Ljava/lang/String; + public fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getConnectionConfig (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionType;)Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig; + public final fun getDatabase ()Ljava/lang/String; + public final fun getSocketConnectionConfig ()Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket; + public final fun getSocketConnectionPassword ()Ljava/lang/String; + public final fun getSocketConnectionPort ()Ljava/lang/Integer; + public final fun getUnixDomainSocketPath ()Ljava/lang/String; + public final fun getUnixDomainSocketWithPeerAuthenticationConnectionConfig ()Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$UnixDomainSocketWithPeerAuthentication; + public final fun getUser ()Ljava/lang/String; +} + +public final class com/huanshankeji/exposedvertxsqlclient/local/LocalConnectionConfig$Companion { +} + +public final class com/huanshankeji/exposedvertxsqlclient/local/LocalConnectionConfigKt { + public static final fun toPerformantUnixEvscConfig (Lcom/huanshankeji/exposedvertxsqlclient/local/LocalConnectionConfig;)Lcom/huanshankeji/exposedvertxsqlclient/EvscConfig; + public static final fun toUniversalEvscConfig (Lcom/huanshankeji/exposedvertxsqlclient/local/LocalConnectionConfig;)Lcom/huanshankeji/exposedvertxsqlclient/EvscConfig; +} + +public final class com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlClientsKt { + public static final fun createGenericSqlClient (Lio/vertx/core/Vertx;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lio/vertx/sqlclient/SqlConnectOptions;Lkotlin/jvm/functions/Function1;Lio/vertx/sqlclient/PoolOptions;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function4;)Lio/vertx/sqlclient/SqlClient; + public static final fun createGenericSqlClientWithBuilder (Lio/vertx/core/Vertx;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lio/vertx/sqlclient/ClientBuilder;Lio/vertx/sqlclient/SqlConnectOptions;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lio/vertx/sqlclient/PoolOptions;)Lio/vertx/sqlclient/SqlClient; + public static final fun createGenericSqlConnection (Lio/vertx/core/Vertx;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lkotlin/jvm/functions/Function2;Lio/vertx/sqlclient/SqlConnectOptions;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnectOptionsKt { + public static final fun setFrom (Lio/vertx/sqlclient/SqlConnectOptions;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;)V + public static final fun setFrom (Lio/vertx/sqlclient/SqlConnectOptions;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$UnixDomainSocketWithPeerAuthentication;)V + public static final fun setFrom (Lio/vertx/sqlclient/SqlConnectOptions;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;)V +} + +public final class com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnectionInitializationKt { + public static final fun getCoConnectHandler (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lkotlin/jvm/functions/Function2;)Lkotlin/jvm/functions/Function2; + public static final fun initConnection (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lio/vertx/sqlclient/SqlConnection;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun toWithParameterFunction (Lkotlin/jvm/functions/Function2;)Lkotlin/jvm/functions/Function2; +} + +public final class com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnectionKt { + public static final fun setRole (Lio/vertx/sqlclient/SqlConnection;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + diff --git a/core/build.gradle.kts b/core/build.gradle.kts new file mode 100644 index 0000000..5b5c918 --- /dev/null +++ b/core/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + `lib-conventions` +} + +dependencies { + api(commonDependencies.exposed.core()) // TODO: use `implementation` when possible + api(commonDependencies.kotlinCommon.exposed()) + + with(commonDependencies.vertx) { + implementation(platformStackDepchain()) + api(moduleWithoutVersion("sql-client")) // TODO: use `implementation` when possible + implementation(moduleWithoutVersion("lang-kotlin")) + implementation(moduleWithoutVersion("lang-kotlin-coroutines")) + } + implementation(commonDependencies.kotlinCommon.vertx()) + + implementation(commonDependencies.kotlinCommon.core()) + implementation(commonDependencies.arrow.core()) + + implementation(commonDependencies.kotlinCommon.net()) + + api(commonDependencies.kotlinCommon.coroutines()) // Exposed the `use` function +} diff --git a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionConfig.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionConfig.kt similarity index 77% rename from lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionConfig.kt rename to core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionConfig.kt index 09c7365..cfbb80a 100644 --- a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionConfig.kt +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionConfig.kt @@ -1,5 +1,7 @@ package com.huanshankeji.exposedvertxsqlclient +import com.huanshankeji.exposedvertxsqlclient.vertx.sqlclient.setRole + sealed interface ConnectionConfig { val userAndRole: String val database: String @@ -14,6 +16,9 @@ sealed interface ConnectionConfig { override val userAndRole: String get() = user } + /** + * @see setRole + */ class UnixDomainSocketWithPeerAuthentication( val path: String, val role: String, @@ -21,4 +26,7 @@ sealed interface ConnectionConfig { ) : ConnectionConfig { override val userAndRole: String get() = role } -} \ No newline at end of file +} + +fun ConnectionConfig.Socket.toUniversalEvscConfig() = + EvscConfig(this, this) diff --git a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionType.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionType.kt similarity index 71% rename from lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionType.kt rename to core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionType.kt index 54d1b56..03ff607 100644 --- a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionType.kt +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ConnectionType.kt @@ -1,5 +1,6 @@ package com.huanshankeji.exposedvertxsqlclient +//@Deprecated("This class seems no longer used.") enum class ConnectionType { Socket, UnixDomainSocketWithPeerAuthentication } \ No newline at end of file diff --git a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/DatabaseClient.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/DatabaseClient.kt similarity index 73% rename from lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/DatabaseClient.kt rename to core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/DatabaseClient.kt index f66ac8a..ba3b423 100644 --- a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/DatabaseClient.kt +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/DatabaseClient.kt @@ -1,26 +1,18 @@ package com.huanshankeji.exposedvertxsqlclient import arrow.core.* +import com.huanshankeji.ExperimentalApi import com.huanshankeji.collections.singleOrNullIfEmpty -import com.huanshankeji.exposedvertxsqlclient.ConnectionConfig.Socket -import com.huanshankeji.exposedvertxsqlclient.ConnectionConfig.UnixDomainSocketWithPeerAuthentication -import com.huanshankeji.exposedvertxsqlclient.sql.selectExpression -import com.huanshankeji.os.isOSLinux +import com.huanshankeji.kotlinx.coroutine.CoroutineAutoCloseable import com.huanshankeji.vertx.kotlin.coroutines.coroutineToFuture import com.huanshankeji.vertx.kotlin.sqlclient.executeBatchAwaitForSqlResultSequence -import io.vertx.core.Vertx import io.vertx.core.buffer.Buffer import io.vertx.kotlin.coroutines.coAwait -import io.vertx.kotlin.sqlclient.poolOptionsOf -import io.vertx.pgclient.PgConnectOptions -import io.vertx.pgclient.PgConnection -import io.vertx.pgclient.PgPool import io.vertx.sqlclient.* import kotlinx.coroutines.coroutineScope import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.Query -import org.jetbrains.exposed.sql.Transaction import org.jetbrains.exposed.sql.statements.InsertStatement import org.jetbrains.exposed.sql.statements.Statement import org.jetbrains.exposed.sql.statements.UpdateStatement @@ -73,8 +65,8 @@ fun String.toVertxPgClientPreparedSql(): String { fun Statement<*>.getVertxPgClientPreparedSql(transaction: ExposedTransaction) = prepareSQL(transaction).toVertxPgClientPreparedSql() - -internal fun dbAssert(b: Boolean) { +@InternalApi +fun dbAssert(b: Boolean) { if (!b) throw AssertionError() } @@ -85,30 +77,33 @@ internal val logger = LoggerFactory.getLogger(DatabaseClient::class.java) /** * A wrapper client around Vert.x [SqlClient] for queries and an Exposed [Database] to generate SQLs working around the limitations of Exposed. * - * @param validateBatch whether to validate whether the batch statements have the same generated prepared SQL. + * @param validateBatch whether to validate whether the batch statements have the same generated prepared SQL. It's recommended to keep this enabled for tests but disabled for production. */ -@OptIn(ExperimentalEvscApi::class) -class DatabaseClient( - val vertxSqlClient: VertxSqlClient, +@OptIn(ExperimentalApi::class) +class DatabaseClient( + val vertxSqlClient: VertxSqlClientT, val exposedDatabase: Database, + // TODO consider adding a `isProduction` parameter whose default depends on the runtime val validateBatch: Boolean = true, val logSql: Boolean = false -) { - suspend fun close() { +) : CoroutineAutoCloseable { + override suspend fun close() { vertxSqlClient.close().coAwait() // How to close The Exposed `Database`? } + // TODO consider splitting into 2, one with `readOnly` set to true and isolation level `NONE` / READ UNCOMMITED for SQL generation, and a normal one for Exposed execution + // TODO also consider adding the 2 parameters `transactionIsolation` and `readOnly` with default arguments fun exposedTransaction(statement: ExposedTransaction.() -> T) = transaction(exposedDatabase, statement) - private fun Statement<*>.prepareSqlAndLogIfNeeded(transaction: Transaction) = + private fun Statement<*>.prepareSqlAndLogIfNeeded(transaction: ExposedTransaction) = prepareSQL(transaction).also { if (logSql) logger.info("Prepared SQL: $it.") } suspend fun executePlainSql(sql: String): RowSet = - /** Use [SqlClient.preparedQuery] here because of [PgConnectOptions.setCachePreparedStatements]. */ + /** Use [SqlClient.preparedQuery] here because of [SqlConnectOptions.setCachePreparedStatements]. */ vertxSqlClient.preparedQuery(sql).execute().coAwait() suspend fun executePlainSqlUpdate(sql: String): Int = @@ -123,7 +118,12 @@ class DatabaseClient( */ @Deprecated( "This function does not support analyzing dependencies among tables. Since this action is not frequently needed we can adopt the blocking approach. Use Exposed `SchemaUtils` and create multiple tables in batch instead, temporarily.", - ReplaceWith("exposedTransaction { SchemaUtils.create(table) }", "org.jetbrains.exposed.sql.SchemaUtils") + ReplaceWith( + "withContext(Dispatchers.IO) { exposedTransaction { SchemaUtils.create(table) } }", + "kotlinx.coroutines.withContext", + "kotlinx.coroutines.Dispatchers", + "org.jetbrains.exposed.sql.SchemaUtils" + ) ) suspend fun createTable(table: Table) = executePlainSqlUpdate(exposedTransaction { @@ -133,7 +133,13 @@ class DatabaseClient( @Deprecated( "This function does not support analyzing dependencies among tables. Since this action is not frequently needed we can adopt the blocking approach. Use Exposed `SchemaUtils` and drop multiple tables in batch instead, temporarily.", - ReplaceWith("exposedTransaction { SchemaUtils.drop(table) }", "org.jetbrains.exposed.sql.SchemaUtils") + // TODO `Dispatchers.IO` + ReplaceWith( + "withContext(Dispatchers.IO) { exposedTransaction { SchemaUtils.drop(table) } }", + "kotlinx.coroutines.withContext", + "kotlinx.coroutines.Dispatchers", + "org.jetbrains.exposed.sql.SchemaUtils" + ) ) suspend fun dropTable(table: Table) = executePlainSqlUpdate(exposedTransaction { @@ -205,20 +211,20 @@ class DatabaseClient( @Deprecated( - "Use `selectExpression` instead`", + "Use `selectExpression` in the \"sql-dsl\" module instead.", ReplaceWith( "selectExpression(clazz, expression)", "com.huanshankeji.exposedvertxsqlclient.sql.selectExpression" ) ) suspend fun executeExpression(clazz: KClass, expression: Expression): T? = - selectExpression(clazz, expression) + throw NotImplementedError() @Deprecated( - "Use `selectExpression` instead`", + "Use `selectExpression` in the \"sql-dsl\" module instead.", ReplaceWith("selectExpression(expression)", "com.huanshankeji.exposedvertxsqlclient.sql.selectExpression") ) suspend inline fun executeExpression(expression: Expression): T = - selectExpression(expression) + throw NotImplementedError() suspend fun isWorking(): Boolean = try { @@ -290,6 +296,9 @@ class DatabaseClient( suspend fun executeBatchForVertxSqlClientRowSetSequence(statements: Iterable>): Sequence> = executeBatch(statements) { this } + /** + * @see executeBatch + */ @ExperimentalEvscApi suspend inline fun executeBatchQuery( fieldSet: FieldSet, queries: Iterable, crossinline resultRowMapper: ResultRow.() -> Data @@ -310,7 +319,7 @@ class DatabaseClient( */ /** * Executes a batch of update statements, including [InsertStatement] and [UpdateStatement]. - * @see org.jetbrains.exposed.sql.batchInsert + * @see executeBatch * @return a sequence of the update counts of the update statements in the batch. */ suspend fun executeBatchUpdate( @@ -384,20 +393,20 @@ fun Int.singleOrNoUpdate() = * When using this function, it's recommended to name the lambda parameter the same as the outer receiver so that the outer [DatabaseClient] is shadowed, * and so that you don't call the outer [DatabaseClient] without a transaction by accident. */ -suspend fun DatabaseClient.withTransaction(function: suspend (DatabaseClient) -> T): T = +suspend fun DatabaseClient.withTransaction(function: suspend (DatabaseClient) -> T): T = coroutineScope { vertxSqlClient.withTransaction { coroutineToFuture { function(DatabaseClient(it, exposedDatabase)) } }.coAwait() } -suspend fun DatabaseClient.withPgTransaction(function: suspend (DatabaseClient) -> T): T = +suspend fun DatabaseClient.withTypedTransaction(function: suspend (DatabaseClient) -> T): T = withTransaction { @Suppress("UNCHECKED_CAST") - function(it as DatabaseClient) + function(it as DatabaseClient) } -suspend fun DatabaseClient.withTransactionCommitOrRollback(function: suspend (DatabaseClient) -> Option): Option { +suspend fun DatabaseClient.withTransactionCommitOrRollback(function: suspend (DatabaseClient) -> Option): Option { val transaction = vertxSqlClient.begin().coAwait() return try { val result = function(this) @@ -414,21 +423,21 @@ suspend fun DatabaseClient.withTransactionCommitOrRollback(fu val savepointNameRegex = Regex("\\w+") -private suspend fun DatabaseClient.savepoint(savepointName: String) = +private suspend fun DatabaseClient.savepoint(savepointName: String) = executePlainSqlUpdate("SAVEPOINT \"$savepointName\"").also { dbAssert(it == 0) } -private suspend fun DatabaseClient.rollbackToSavepoint(savepointName: String) = +private suspend fun DatabaseClient.rollbackToSavepoint(savepointName: String) = executePlainSqlUpdate("ROLLBACK TO SAVEPOINT \"$savepointName\"").also { dbAssert(it == 0) } -private suspend fun DatabaseClient.releaseSavepoint(savepointName: String) = +private suspend fun DatabaseClient.releaseSavepoint(savepointName: String) = executePlainSqlUpdate("RELEASE SAVEPOINT \"$savepointName\"").also { dbAssert(it == 0) } /** - * Currently only available for PostgreSQL. + * Not tested yet on DBs other than PostgreSQL. * A savepoint destroys one with the same name so be careful. */ -suspend fun DatabaseClient.withSavepointAndRollbackIfThrowsOrLeft( - savepointName: String, function: suspend (DatabaseClient) -> Either +suspend fun DatabaseClient.withSavepointAndRollbackIfThrowsOrLeft( + savepointName: String, function: suspend (DatabaseClient) -> Either ): Either { // Prepared query seems not to work here. @@ -448,89 +457,17 @@ suspend fun DatabaseClient.withSavepointAndR } } -suspend fun DatabaseClient.withSavepointAndRollbackIfThrows( - savepointName: String, function: suspend (DatabaseClient) -> T +suspend fun DatabaseClient.withSavepointAndRollbackIfThrows( + savepointName: String, function: suspend (DatabaseClient) -> T ): T = withSavepointAndRollbackIfThrowsOrLeft(savepointName) { function(it).right() }.getOrElse { throw AssertionError() } -suspend fun DatabaseClient.withSavepointAndRollbackIfThrowsOrNone( - savepointName: String, function: suspend (DatabaseClient) -> Option +suspend fun DatabaseClient.withSavepointAndRollbackIfThrowsOrNone( + savepointName: String, function: suspend (DatabaseClient) -> Option ): Option = withSavepointAndRollbackIfThrowsOrLeft(savepointName) { function(it).toEither { } }.getOrNone() -suspend fun DatabaseClient.withSavepointAndRollbackIfThrowsOrFalse( - savepointName: String, function: suspend (DatabaseClient) -> Boolean +suspend fun DatabaseClient.withSavepointAndRollbackIfThrowsOrFalse( + savepointName: String, function: suspend (DatabaseClient) -> Boolean ): Boolean = withSavepointAndRollbackIfThrowsOrLeft(savepointName) { if (function(it)) Unit.right() else Unit.left() }.isRight() - -// TODO: use `ConnectionConfig` as the argument directly - -// can be used for a shared Exposed `Database` among `DatabaseClient`s -fun createPgPoolDatabaseClient( - vertx: Vertx?, - vertxSqlClientConnectionConfig: ConnectionConfig, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptions = poolOptionsOf(), - exposedDatabase: Database -): DatabaseClient = - DatabaseClient( - with(vertxSqlClientConnectionConfig) { - when (this) { - is Socket -> - createSocketPgPool(vertx, host, port, database, user, password, extraPgConnectOptions, poolOptions) - - is UnixDomainSocketWithPeerAuthentication -> - createPeerAuthenticationUnixDomainSocketPgPoolAndSetRole( - vertx, path, database, role, extraPgConnectOptions, poolOptions - ) - } - }, - exposedDatabase - ) - -fun createPgPoolDatabaseClient( - vertx: Vertx?, - vertxSqlClientConnectionConfig: ConnectionConfig, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptions = poolOptionsOf(), - exposedSocketConnectionConfig: Socket -): DatabaseClient = - createPgPoolDatabaseClient( - vertx, vertxSqlClientConnectionConfig, extraPgConnectOptions, poolOptions, - exposedDatabaseConnectPostgreSql(exposedSocketConnectionConfig) - ) - -/** It may be more efficient to use a single shared [Database] to generate SQLs for multiple [DatabaseClient]s/[SqlClient]s. */ -fun createPgPoolDatabaseClient( - vertx: Vertx?, - vertxSqlClientConnectionType: ConnectionType, localConnectionConfig: LocalConnectionConfig, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptions = poolOptionsOf(), - exposedDatabase: Database? = null -) = - with(localConnectionConfig) { - val connectionConfig = when (vertxSqlClientConnectionType) { - ConnectionType.Socket -> socketConnectionConfig - ConnectionType.UnixDomainSocketWithPeerAuthentication -> unixDomainSocketWithPeerAuthenticationConnectionConfig - } - - if (exposedDatabase === null) - createPgPoolDatabaseClient( - vertx, connectionConfig, extraPgConnectOptions, poolOptions, socketConnectionConfig - ) - else - createPgPoolDatabaseClient( - vertx, connectionConfig, extraPgConnectOptions, poolOptions, exposedDatabase - ) - } - -fun createBetterPgPoolDatabaseClient( - vertx: Vertx?, - localConnectionConfig: LocalConnectionConfig, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptions = poolOptionsOf(), - exposedDatabase: Database? = null -) = - createPgPoolDatabaseClient( - vertx, - if (isOSLinux()) ConnectionType.UnixDomainSocketWithPeerAuthentication else ConnectionType.Socket, - localConnectionConfig, - extraPgConnectOptions, poolOptions, - exposedDatabase - ) diff --git a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/EvscConfig.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/EvscConfig.kt similarity index 78% rename from lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/EvscConfig.kt rename to core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/EvscConfig.kt index fae1efb..fcb66e2 100644 --- a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/EvscConfig.kt +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/EvscConfig.kt @@ -6,8 +6,9 @@ interface IEvscConfig { val vertxSqlClientConnectionConfig: ConnectionConfig } +// TODO add a type parameter for `exposedConnectionConfig` to better support RDBMSs that don't support Unix domain sockets /** - * This API is not used in the factory function parameter types yet. TODO + * This API is not used in the factory function parameter types yet. */ @ExperimentalEvscApi class EvscConfig( diff --git a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ExperimentalEvscApi.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ExperimentalEvscApi.kt similarity index 77% rename from lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ExperimentalEvscApi.kt rename to core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ExperimentalEvscApi.kt index a63caa0..668e4c0 100644 --- a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ExperimentalEvscApi.kt +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ExperimentalEvscApi.kt @@ -2,7 +2,7 @@ package com.huanshankeji.exposedvertxsqlclient import kotlin.annotation.AnnotationTarget.* -@RequiresOptIn("This API is experimental in the Exposed Vert.x SQL Client library.", RequiresOptIn.Level.WARNING) +@RequiresOptIn("This API is experimental. It may be changed in the future without notice.", RequiresOptIn.Level.WARNING) @Retention(AnnotationRetention.BINARY) // The ones commented out are what I think may be used in very few use cases. @Target( diff --git a/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/InternalApi.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/InternalApi.kt new file mode 100644 index 0000000..cbe7d6d --- /dev/null +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/InternalApi.kt @@ -0,0 +1,5 @@ +package com.huanshankeji.exposedvertxsqlclient + +@RequiresOptIn("This API is internal in the Exposed Vert.x SQL Client library. It may be changed in the future without notice.") +@Retention(AnnotationRetention.BINARY) +annotation class InternalApi \ No newline at end of file diff --git a/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/exposed/ExposedDatabases.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/exposed/ExposedDatabases.kt new file mode 100644 index 0000000..c9c4b92 --- /dev/null +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/exposed/ExposedDatabases.kt @@ -0,0 +1,35 @@ +package com.huanshankeji.exposedvertxsqlclient.exposed + +import com.huanshankeji.exposedvertxsqlclient.ConnectionConfig +import com.huanshankeji.exposedvertxsqlclient.ExperimentalEvscApi +import com.huanshankeji.exposedvertxsqlclient.jdbc.jdbcUrl +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.DatabaseConfig +import org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManager +import org.jetbrains.exposed.sql.transactions.TransactionManager +import java.sql.Connection + +/** + * Further configurations such as [setupConnection], [databaseConfig], and [manager] are most likely not needed + * because the Exposed [Database] is mostly only used for table creation and SQL generation. + */ +@ExperimentalEvscApi +fun ConnectionConfig.Socket.exposedDatabaseConnect( + rdbms: String, + driver: String, + setupConnection: (Connection) -> Unit = {}, + databaseConfig: DatabaseConfig? = null, + manager: (Database) -> TransactionManager = { ThreadLocalTransactionManager(it) } +) = + Database.connect(jdbcUrl(rdbms), driver, user, password, setupConnection, databaseConfig, manager) + +@ExperimentalEvscApi +fun exposedDatabaseConnect( + rdbms: String, + socketConnectionConfig: ConnectionConfig.Socket, + driver: String, + setupConnection: (Connection) -> Unit = {}, + databaseConfig: DatabaseConfig? = null, + manager: (Database) -> TransactionManager = { ThreadLocalTransactionManager(it) } +) = + socketConnectionConfig.exposedDatabaseConnect(rdbms, driver, setupConnection, databaseConfig, manager) diff --git a/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/jdbc/JdbcUrls.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/jdbc/JdbcUrls.kt new file mode 100644 index 0000000..be8bcdd --- /dev/null +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/jdbc/JdbcUrls.kt @@ -0,0 +1,7 @@ +package com.huanshankeji.exposedvertxsqlclient.jdbc + +import com.huanshankeji.exposedvertxsqlclient.ConnectionConfig +import com.huanshankeji.jdbc.jdbcUrl + +fun ConnectionConfig.Socket.jdbcUrl(rdbms: String) = + jdbcUrl(rdbms, host, port, database) diff --git a/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/local/LocalConnectionConfig.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/local/LocalConnectionConfig.kt new file mode 100644 index 0000000..997c52b --- /dev/null +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/local/LocalConnectionConfig.kt @@ -0,0 +1,45 @@ +package com.huanshankeji.exposedvertxsqlclient.local + +import com.huanshankeji.exposedvertxsqlclient.ConnectionConfig +import com.huanshankeji.exposedvertxsqlclient.ConnectionType +import com.huanshankeji.exposedvertxsqlclient.EvscConfig +import com.huanshankeji.exposedvertxsqlclient.ExperimentalEvscApi +import com.huanshankeji.net.LOCALHOST + +// TODO consider adding a prefix word such as "conventional" as this class is not general enough +// TODO consider refactoring this class into a function / functions to reduce the cognitive complexity +/** + * A kind of connection config that can produce both a [ConnectionConfig.Socket] and a [ConnectionConfig.UnixDomainSocketWithPeerAuthentication] + * to connect to a local database server. + */ +class LocalConnectionConfig( + val socketConnectionPort: Int? = null, + val unixDomainSocketPath: String, + val user: String, + val socketConnectionPassword: String, + val database: String +) { + companion object { + const val SOCKET_HOST = LOCALHOST + } + + val socketConnectionConfig = + ConnectionConfig.Socket(SOCKET_HOST, socketConnectionPort, user, socketConnectionPassword, database) + + val unixDomainSocketWithPeerAuthenticationConnectionConfig = + ConnectionConfig.UnixDomainSocketWithPeerAuthentication(unixDomainSocketPath, user, database) + + fun getConnectionConfig(connectionType: ConnectionType) = + when (connectionType) { + ConnectionType.Socket -> socketConnectionConfig + ConnectionType.UnixDomainSocketWithPeerAuthentication -> unixDomainSocketWithPeerAuthenticationConnectionConfig + } +} + +@ExperimentalEvscApi +fun LocalConnectionConfig.toPerformantUnixEvscConfig() = + EvscConfig(socketConnectionConfig, unixDomainSocketWithPeerAuthenticationConnectionConfig) + +@ExperimentalEvscApi +fun LocalConnectionConfig.toUniversalEvscConfig() = + EvscConfig(socketConnectionConfig, socketConnectionConfig) diff --git a/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlClients.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlClients.kt new file mode 100644 index 0000000..2e79cb5 --- /dev/null +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlClients.kt @@ -0,0 +1,98 @@ +package com.huanshankeji.exposedvertxsqlclient.vertx.sqlclient + +import com.huanshankeji.exposedvertxsqlclient.ConnectionConfig +import com.huanshankeji.exposedvertxsqlclient.ExperimentalEvscApi +import com.huanshankeji.vertx.sqlclient.setUpConventionally +import com.huanshankeji.vertx.sqlclient.withCoConnectHandler +import io.vertx.core.Future +import io.vertx.core.Vertx +import io.vertx.kotlin.coroutines.coAwait +import io.vertx.sqlclient.* + +/** + * Exposed generates prepared statements and [SqlConnectOptions.cachePreparedStatements] improves performance greatly (tested on PostgreSQL) + * so it's enabled by default. + */ +@ExperimentalEvscApi +// made inline for possible suspend calls +// TODO consider removing the default arguments so we don't forget to pass them in this library's functions +inline fun createGenericSqlClient( + vertx: Vertx?, + connectionConfig: ConnectionConfig, + sqlConnectOptionsFromConstructor: SqlConnectOptionsT, + extraSqlConnectOptions: SqlConnectOptionsT.() -> Unit, + poolOptionsFromConstructor: PoolOptionsT, + extraPoolOptions: PoolOptionsT.() -> Unit, + noinline connectHandlerExtra: CoConnectHandler, + create: (Vertx?, SqlConnectOptionsT, PoolOptionsT, CoConnectHandler) -> SqlClientT +): SqlClientT { + val sqlConnectOptions = sqlConnectOptionsFromConstructor.apply { + setUpConventionally() + setFrom(connectionConfig) + extraSqlConnectOptions() + } + + return create(vertx, sqlConnectOptions, poolOptionsFromConstructor.apply(extraPoolOptions), connectHandlerExtra) +} + +/** + * @see createGenericSqlClient + */ +@ExperimentalEvscApi +// made not inline anymore for easier debugging +fun > createGenericSqlClientWithBuilder( + vertx: Vertx?, + connectionConfig: ConnectionConfig, + clientBuilder: ClientBuilderT, + sqlConnectOptionsFromConstructor: SqlConnectOptionsT, + extraSqlConnectOptions: SqlConnectOptionsT.() -> Unit, + extraPoolOptions: PoolOptionsT.() -> Unit, + connectHandlerExtra: CoConnectHandler, + poolOptionsFromConstructor: PoolOptionsT +): SqlClientT = + @Suppress("NAME_SHADOWING") + createGenericSqlClient( + vertx, + connectionConfig, + sqlConnectOptionsFromConstructor, + extraSqlConnectOptions, + poolOptionsFromConstructor, + extraPoolOptions, + connectHandlerExtra + ) { vertx, database, options, connectHandlerExtra -> + clientBuilder.apply { + using(vertx) + connectingTo(database) + with(options) + val connectHandler = connectionConfig.getCoConnectHandler(connectHandlerExtra) + connectHandler?.let { withCoConnectHandler(it) } + }.build() + } + +/** + * @see createGenericSqlClient + */ +@ExperimentalEvscApi +// made not inline anymore for easier debugging +suspend fun createGenericSqlConnection( + vertx: Vertx?, + connectionConfig: ConnectionConfig, + sqlConnectionConnect: (Vertx?, SqlConnectOptionsT) -> Future, + sqlConnectOptionsFromConstructor: SqlConnectOptionsT, + extraSqlConnectOptions: SqlConnectOptionsT.() -> Unit, + connectHandlerExtra: CoConnectHandler +): SqlConnectionT = + @Suppress("NAME_SHADOWING") + createGenericSqlClient( + vertx, + connectionConfig, + sqlConnectOptionsFromConstructor, + extraSqlConnectOptions, + null, + {}, + connectHandlerExtra + ) { vertx, database, _, connectHandlerExtra -> + sqlConnectionConnect(vertx, database).coAwait().also { + connectionConfig.initConnection(it, connectHandlerExtra) + } + } diff --git a/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnectOptions.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnectOptions.kt new file mode 100644 index 0000000..59384a0 --- /dev/null +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnectOptions.kt @@ -0,0 +1,24 @@ +package com.huanshankeji.exposedvertxsqlclient.vertx.sqlclient + +import com.huanshankeji.exposedvertxsqlclient.ConnectionConfig +import io.vertx.sqlclient.SqlConnectOptions + +fun SqlConnectOptions.setFrom(connectionConfig: ConnectionConfig.Socket) { + host = connectionConfig.host + connectionConfig.port?.let { port = it } + database = connectionConfig.database + user = connectionConfig.user + password = connectionConfig.password +} + +fun SqlConnectOptions.setFrom(connectionConfig: ConnectionConfig.UnixDomainSocketWithPeerAuthentication) { + host = connectionConfig.path + database = connectionConfig.database + user = System.getProperty("user.name") +} + +fun SqlConnectOptions.setFrom(connectionConfig: ConnectionConfig) = + when (connectionConfig) { + is ConnectionConfig.Socket -> setFrom(connectionConfig) + is ConnectionConfig.UnixDomainSocketWithPeerAuthentication -> setFrom(connectionConfig) + } diff --git a/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnection.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnection.kt new file mode 100644 index 0000000..1b4983d --- /dev/null +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnection.kt @@ -0,0 +1,12 @@ +package com.huanshankeji.exposedvertxsqlclient.vertx.sqlclient + +import com.huanshankeji.ExperimentalApi +import io.vertx.kotlin.coroutines.coAwait +import io.vertx.sqlclient.SqlConnection + +// TODO consider using prepared statements +// TODO assert and return `Unit` +// TODO consider moving to "kotlin-common" +@ExperimentalApi +suspend fun SqlConnection.setRole(role: String) = + query("SET ROLE $role").execute().coAwait() diff --git a/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnectionInitialization.kt b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnectionInitialization.kt new file mode 100644 index 0000000..c228c6a --- /dev/null +++ b/core/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/vertx/sqlclient/SqlConnectionInitialization.kt @@ -0,0 +1,32 @@ +package com.huanshankeji.exposedvertxsqlclient.vertx.sqlclient + +import com.huanshankeji.exposedvertxsqlclient.ConnectionConfig +import io.vertx.sqlclient.SqlConnection + +/** + * Extra initialization on [SqlConnection] in addition to [setRole] for [ConnectionConfig.UnixDomainSocketWithPeerAuthentication]. + */ +typealias CoConnectHandler = (suspend (SqlConnection) -> Unit)? +typealias ExtensionCoConnectHandler = (suspend SqlConnection.() -> Unit)? + +fun ExtensionCoConnectHandler.toWithParameterFunction(): CoConnectHandler = + this + +suspend fun ConnectionConfig.initConnection(sqlConnection: SqlConnection, extra: CoConnectHandler) { + when (this) { + is ConnectionConfig.Socket -> Unit // do nothing + is ConnectionConfig.UnixDomainSocketWithPeerAuthentication -> sqlConnection.setRole(role) + } + extra?.let { it(sqlConnection) } +} + +fun ConnectionConfig.getCoConnectHandler(extra: CoConnectHandler): CoConnectHandler = + when (this) { + is ConnectionConfig.Socket -> extra + is ConnectionConfig.UnixDomainSocketWithPeerAuthentication -> { + { + it.setRole(role) + extra?.let { extra -> extra(it) } + } + } + } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..4a50b9e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +# for Dokka +org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled diff --git a/integrated/README.md b/integrated/README.md new file mode 100644 index 0000000..64e0db5 --- /dev/null +++ b/integrated/README.md @@ -0,0 +1,3 @@ +# The integrated module + +This module includes tests, benchmarks, and examples. diff --git a/integrated/api/exposed-vertx-sql-client-integrated.api b/integrated/api/exposed-vertx-sql-client-integrated.api new file mode 100644 index 0000000..a422a43 --- /dev/null +++ b/integrated/api/exposed-vertx-sql-client-integrated.api @@ -0,0 +1,62 @@ +public final class com/huanshankeji/exposedvertxsqlclient/Alternative { + public static final field INSTANCE Lcom/huanshankeji/exposedvertxsqlclient/Alternative; + public final fun getEvscConfig ()Lcom/huanshankeji/exposedvertxsqlclient/EvscConfig; +} + +public final class com/huanshankeji/exposedvertxsqlclient/Director { + public fun (ILjava/lang/String;)V + public final fun getDirectorId ()I + public final fun getName ()Ljava/lang/String; +} + +public final class com/huanshankeji/exposedvertxsqlclient/Directors : org/jetbrains/exposed/dao/id/IntIdTable { + public static final field INSTANCE Lcom/huanshankeji/exposedvertxsqlclient/Directors; + public final fun getDirectorId ()Lorg/jetbrains/exposed/sql/Column; + public final fun getName ()Lorg/jetbrains/exposed/sql/Column; +} + +public final class com/huanshankeji/exposedvertxsqlclient/Examples : org/jetbrains/exposed/dao/id/IntIdTable { + public static final field INSTANCE Lcom/huanshankeji/exposedvertxsqlclient/Examples; + public final fun getName ()Lorg/jetbrains/exposed/sql/Column; +} + +public final class com/huanshankeji/exposedvertxsqlclient/ExamplesKt { + public static final fun examples (Lio/vertx/core/Vertx;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun getEvscConfig ()Lcom/huanshankeji/exposedvertxsqlclient/EvscConfig; + public static final fun getTables ()[Lcom/huanshankeji/exposedvertxsqlclient/Examples; +} + +public final class com/huanshankeji/exposedvertxsqlclient/Film { + public fun (ILcom/huanshankeji/exposedvertxsqlclient/FilmDetails;)V + public final fun getFilmDetails ()Lcom/huanshankeji/exposedvertxsqlclient/FilmDetails; + public final fun getFilmId ()I +} + +public final class com/huanshankeji/exposedvertxsqlclient/FilmDetails { + public fun (ILjava/lang/String;Ljava/lang/Object;)V + public final fun getDirector ()Ljava/lang/Object; + public final fun getName ()Ljava/lang/String; + public final fun getSequelId ()I +} + +public final class com/huanshankeji/exposedvertxsqlclient/Films : org/jetbrains/exposed/dao/id/IntIdTable { + public static final field INSTANCE Lcom/huanshankeji/exposedvertxsqlclient/Films; + public final fun getDirectorId ()Lorg/jetbrains/exposed/sql/Column; + public final fun getFilmId ()Lorg/jetbrains/exposed/sql/Column; + public final fun getName ()Lorg/jetbrains/exposed/sql/Column; + public final fun getSequelId ()Lorg/jetbrains/exposed/sql/Column; +} + +public final class com/huanshankeji/exposedvertxsqlclient/Mappers { + public static final field INSTANCE Lcom/huanshankeji/exposedvertxsqlclient/Mappers; + public final fun getDirector ()Lcom/huanshankeji/exposed/datamapping/classproperty/ReflectionBasedClassPropertyDataMapper; + public final fun getFilmDetailsWithDirectorId ()Lcom/huanshankeji/exposed/datamapping/classproperty/ReflectionBasedClassPropertyDataMapper; + public final fun getFilmWithDirectorId ()Lcom/huanshankeji/exposed/datamapping/classproperty/ReflectionBasedClassPropertyDataMapper; + public final fun getFullFilm ()Lcom/huanshankeji/exposed/datamapping/classproperty/ReflectionBasedClassPropertyDataMapper; +} + +public final class com/huanshankeji/exposedvertxsqlclient/MappingExamplesKt { + public static final fun getFilmsLeftJoinDirectors ()Lorg/jetbrains/exposed/sql/Join; + public static final fun mappingExamples (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + diff --git a/integrated/build.gradle.kts b/integrated/build.gradle.kts new file mode 100644 index 0000000..4f95b3c --- /dev/null +++ b/integrated/build.gradle.kts @@ -0,0 +1,34 @@ +// TODO consider moving the code related to only Exposed or Vert.x to "kotlin-common" + +import com.huanshankeji.cpnProject + +plugins { + conventions + id("com.huanshankeji.benchmark.kotlinx-benchmark-jvm-conventions") +} + +dependencies { + with(commonDependencies.vertx) { implementation(platformStackDepchain()) } // needed + + implementation(cpnProject(project, ":core")) + implementation(cpnProject(project, ":postgresql")) + implementation(cpnProject(project, ":sql-dsl")) + implementation("com.huanshankeji:exposed-adt-mapping:${DependencyVersions.exposedAdtMapping}") + implementation(cpnProject(project, ":sql-dsl-with-mapper")) +} + +afterEvaluate { +// for the benchmarks + dependencies { + with(commonDependencies.vertx) { "benchmarksImplementation"(platformStackDepchain()) } // needed + // The benchmarks run and "check" passes but the code doesn't resolve without this dependency TODO remove if not needed one day + "benchmarksImplementation"(cpnProject(project, ":core")) + "benchmarksImplementation"(cpnProject(project, ":postgresql")) + + with(commonDependencies.testContainers) { + "benchmarksImplementation"(platformBom()) + "benchmarksImplementation"(postgreSql) + } + "benchmarksImplementation"(commonDependencies.slf4j.simple()) + } +} diff --git a/lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/AbstractBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/AbstractBenchmark.kt similarity index 100% rename from lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/AbstractBenchmark.kt rename to integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/AbstractBenchmark.kt diff --git a/lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/EmptyBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/EmptyBenchmark.kt similarity index 100% rename from lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/EmptyBenchmark.kt rename to integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/EmptyBenchmark.kt diff --git a/lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/PreparedSqlGenerationBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/PreparedSqlGenerationBenchmark.kt similarity index 97% rename from lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/PreparedSqlGenerationBenchmark.kt rename to integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/PreparedSqlGenerationBenchmark.kt index 3110a50..977bec2 100644 --- a/lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/PreparedSqlGenerationBenchmark.kt +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/PreparedSqlGenerationBenchmark.kt @@ -16,7 +16,7 @@ import org.jetbrains.exposed.sql.transactions.transaction class PreparedSqlGenerationBenchmark : WithContainerizedDatabaseBenchmark() { enum class StatementEnum(val statement: Statement<*>) { SelectAll(VarcharTable.selectAll()), - SelectWhere(VarcharTable.select(VarcharTable.id eq 0L)), + SelectWhere(VarcharTable.selectAll().where(VarcharTable.id eq 0L)), Insert(VarcharTable.insertStatement { it[varcharColumn] = "string" }), Update(VarcharTable.updateStatement({ VarcharTable.id eq 0L }) { it[varcharColumn] = "string" diff --git a/lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/StatementBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/StatementBenchmark.kt similarity index 100% rename from lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/StatementBenchmark.kt rename to integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/StatementBenchmark.kt diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt new file mode 100644 index 0000000..858fa47 --- /dev/null +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt @@ -0,0 +1,117 @@ +package com.huanshankeji.exposed.benchmark + +import com.huanshankeji.kotlinx.coroutines.benchmark.ParameterizedRunBlockingAwaitAsyncsBenchmark +import com.huanshankeji.kotlinx.coroutines.benchmark.RunBlockingAwaitAsyncsBenchmark +import kotlinx.benchmark.* +import kotlinx.coroutines.* +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction +import org.jetbrains.exposed.sql.transactions.experimental.suspendedTransactionAsync +import org.jetbrains.exposed.sql.transactions.transaction +import java.util.concurrent.Executors +import kotlin.concurrent.thread + +@State(Scope.Benchmark) +class TransactionBenchmark : WithContainerizedDatabaseBenchmark() { + @Benchmark + fun transaction() { + transaction(database) {} + } + + companion object { + const val `10K` = 10_000 + } + + @Benchmark + fun _10KTransactions() { + repeat(`10K`) { transaction(database) {} } + } + + @Suppress("SuspendFunctionOnCoroutineScope") + private suspend inline fun CoroutineScope.awaitAsync10K(crossinline block: () -> Unit) = + List(`10K`) { async { block() } }.awaitAll() + + @Suppress("SuspendFunctionOnCoroutineScope") + private suspend fun CoroutineScope.awaitAsync10KTransactions() = + awaitAsync10K { transaction(database) {} } + + /** + * Compare with [RunBlockingAwaitAsyncsBenchmark]. + */ + @Benchmark + fun singleThreadConcurrent10KTransactions() = + @OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class) + runBlocking(newSingleThreadContext("single thread")) { + awaitAsync10KTransactions() + } + + + /** + * Compare with [ParameterizedRunBlockingAwaitAsyncsBenchmark]. + */ + @Benchmark + fun multiThreadConcurrent10KTransactionsWithSharedDatabase() = + runBlocking { awaitAsync10KTransactions() } + + + @Benchmark + fun _10KSuspendedTransactions() = runBlocking { + repeat(`10K`) { newSuspendedTransaction(db = database) {} } + } + + @Benchmark + fun _10KSuspendedTransactionAsyncs() = runBlocking { + List(`10K`) { suspendedTransactionAsync(db = database) {} }.awaitAll() + } + + private fun numProcessors() = + Runtime.getRuntime().availableProcessors().also { + println("Number of processors: $it") + } + + @Benchmark + fun multiThreadMultiConnectionEach10KLocalTransactions() { + // Note that on a device with heterogeneous architecture some threads may finish earlier than others. + List(numProcessors()) { + thread { + val database = databaseConnect() + repeat(`10K`) { transaction(database) {} } + } + }.forEach { + it.join() + } + } + + + val databaseThreadLocal = ThreadLocal() + lateinit var dispatcherWithThreadLocalDatabases: ExecutorCoroutineDispatcher + + @Setup + fun setUpThreadLocalDatabases() { + dispatcherWithThreadLocalDatabases = Executors.newFixedThreadPool(numProcessors()) { + Thread { + it.run() + databaseThreadLocal.set(databaseConnect()) + } + }.asCoroutineDispatcher() + } + + @TearDown + fun teardownDispatcherWithThreadLocalDatabases() { + dispatcherWithThreadLocalDatabases.close() + } + + @Benchmark + fun multiThreadConcurrent10KTransactionsWithThreadLocalDatabases() { + runBlocking(dispatcherWithThreadLocalDatabases) { + awaitAsync10K { transaction(databaseThreadLocal.get()) {} } + } + } + + @Benchmark + fun multiThreadConcurrent10KTransactionsWithImplicitThreadLocalDatabases() { + runBlocking(dispatcherWithThreadLocalDatabases) { + awaitAsync10K { transaction {} } + } + } +} \ No newline at end of file diff --git a/lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/WithContainerizedDatabaseBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/WithContainerizedDatabaseBenchmark.kt similarity index 75% rename from lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/WithContainerizedDatabaseBenchmark.kt rename to integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/WithContainerizedDatabaseBenchmark.kt index 9c9cb8d..51e21c7 100644 --- a/lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/WithContainerizedDatabaseBenchmark.kt +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/WithContainerizedDatabaseBenchmark.kt @@ -1,7 +1,7 @@ package com.huanshankeji.exposed.benchmark import com.huanshankeji.exposedvertxsqlclient.ConnectionConfig -import com.huanshankeji.exposedvertxsqlclient.exposedDatabaseConnectPostgreSql +import com.huanshankeji.exposedvertxsqlclient.postgresql.exposed.exposedDatabaseConnectPostgresql import kotlinx.benchmark.Scope import kotlinx.benchmark.Setup import kotlinx.benchmark.State @@ -16,9 +16,8 @@ class WithContainerizedDatabaseBenchmark : AbstractBenchmark() { lateinit var database: Database fun databaseConnect() = - exposedDatabaseConnectPostgreSql(with(postgreSQLContainer) { - ConnectionConfig.Socket(host, firstMappedPort, username, password, databaseName) - }) + with(postgreSQLContainer) { ConnectionConfig.Socket(host, firstMappedPort, username, password, databaseName) } + .exposedDatabaseConnectPostgresql() @Setup fun setUp() { diff --git a/lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/table/Tables.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/table/Tables.kt similarity index 100% rename from lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/table/Tables.kt rename to integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/table/Tables.kt diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractBenchmark.kt new file mode 100644 index 0000000..1220fa9 --- /dev/null +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractBenchmark.kt @@ -0,0 +1,11 @@ +package com.huanshankeji.kotlinx.coroutines.benchmark + +import kotlinx.benchmark.Measurement +import kotlinx.benchmark.Scope +import kotlinx.benchmark.State +import kotlinx.benchmark.Warmup + +@State(Scope.Benchmark) +@Warmup(time = 1, iterations = 8) // 4 seems not enough +@Measurement(time = 1, iterations = 8) +abstract class AbstractBenchmark \ No newline at end of file diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractRunBlockingAwaitAsyncsBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractRunBlockingAwaitAsyncsBenchmark.kt new file mode 100644 index 0000000..a85c4c8 --- /dev/null +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractRunBlockingAwaitAsyncsBenchmark.kt @@ -0,0 +1,7 @@ +package com.huanshankeji.kotlinx.coroutines.benchmark + +abstract class AbstractRunBlockingAwaitAsyncsBenchmark : AbstractBenchmark() { + abstract fun runBlockingAwait1mAsyncs() + + abstract fun runBlockingAwait1KAsync1mSums() +} \ No newline at end of file diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AwaitAsyncs.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AwaitAsyncs.kt new file mode 100644 index 0000000..ebebd26 --- /dev/null +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AwaitAsyncs.kt @@ -0,0 +1,23 @@ +package com.huanshankeji.kotlinx.coroutines.benchmark + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll + +@Suppress("SuspendFunctionOnCoroutineScope") +internal suspend inline fun CoroutineScope.await1mAsyncs() { + List(1 shl 20) { async {} }.awaitAll() +} + +internal val `1mSizeList` = List(1 shl 20) { it } + +// It seems the loop get optimized and removed in this function. +@Suppress("SuspendFunctionOnCoroutineScope") +internal suspend inline fun CoroutineScope.await1kAsync1mLoops() { + List(1 shl 10) { async { repeat(1 shl 20) {} } }.awaitAll() +} + +@Suppress("SuspendFunctionOnCoroutineScope") +internal suspend inline fun CoroutineScope.await1kAsync1mSums() { + List(1 shl 10) { async { `1mSizeList`.sum() } }.awaitAll() +} diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/ParameterizedRunBlockingAwaitAsyncsBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/ParameterizedRunBlockingAwaitAsyncsBenchmark.kt new file mode 100644 index 0000000..92239fa --- /dev/null +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/ParameterizedRunBlockingAwaitAsyncsBenchmark.kt @@ -0,0 +1,34 @@ +package com.huanshankeji.kotlinx.coroutines.benchmark + +import kotlinx.benchmark.Benchmark +import kotlinx.benchmark.Param +import kotlinx.coroutines.* + +class ParameterizedRunBlockingAwaitAsyncsBenchmark : AbstractRunBlockingAwaitAsyncsBenchmark() { + enum class DispatcherArgumentEnum { + Default, /*Main,*/ Unconfined, IO, SingleThread + } + + @Param + lateinit var dispatcherArgumentEnum: DispatcherArgumentEnum + + @OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class) + val singleThreadContext = newSingleThreadContext("single thread") + val dispatcher + get() = when (dispatcherArgumentEnum) { + DispatcherArgumentEnum.Default -> Dispatchers.Default + //DispatcherArgumentEnum.Main -> Dispatchers.Main + DispatcherArgumentEnum.Unconfined -> Dispatchers.Unconfined + DispatcherArgumentEnum.IO -> Dispatchers.IO + DispatcherArgumentEnum.SingleThread -> singleThreadContext + } + + + @Benchmark + override fun runBlockingAwait1mAsyncs() = + runBlocking(dispatcher) { await1mAsyncs() } + + @Benchmark + override fun runBlockingAwait1KAsync1mSums() = + runBlocking(dispatcher) { await1kAsync1mSums() } +} \ No newline at end of file diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/RunBlockingAwaitAsyncsBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/RunBlockingAwaitAsyncsBenchmark.kt new file mode 100644 index 0000000..f0608ce --- /dev/null +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/RunBlockingAwaitAsyncsBenchmark.kt @@ -0,0 +1,14 @@ +package com.huanshankeji.kotlinx.coroutines.benchmark + +import kotlinx.benchmark.Benchmark +import kotlinx.coroutines.runBlocking + +class RunBlockingAwaitAsyncsBenchmark : AbstractRunBlockingAwaitAsyncsBenchmark() { + @Benchmark + override fun runBlockingAwait1mAsyncs() = + runBlocking { await1mAsyncs() } + + @Benchmark + override fun runBlockingAwait1KAsync1mSums() = + runBlocking { await1kAsync1mSums() } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/com/huanshankeji/exposedvertxsqlclient/Examples.kt b/integrated/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/Examples.kt similarity index 55% rename from lib/src/test/kotlin/com/huanshankeji/exposedvertxsqlclient/Examples.kt rename to integrated/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/Examples.kt index d87ca02..b57a5e0 100644 --- a/lib/src/test/kotlin/com/huanshankeji/exposedvertxsqlclient/Examples.kt +++ b/integrated/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/Examples.kt @@ -1,15 +1,22 @@ +@file:OptIn(ExperimentalEvscApi::class) + package com.huanshankeji.exposedvertxsqlclient import com.huanshankeji.exposed.* +import com.huanshankeji.exposedvertxsqlclient.local.toPerformantUnixEvscConfig +import com.huanshankeji.exposedvertxsqlclient.postgresql.exposed.exposedDatabaseConnectPostgresql +import com.huanshankeji.exposedvertxsqlclient.postgresql.local.defaultPostgresqlLocalConnectionConfig +import com.huanshankeji.exposedvertxsqlclient.postgresql.vertx.pgclient.createPgClient +import com.huanshankeji.exposedvertxsqlclient.postgresql.vertx.pgclient.createPgPool import com.huanshankeji.exposedvertxsqlclient.sql.* -import com.huanshankeji.exposedvertxsqlclient.sql.mapping.deleteIgnoreWhere -import com.huanshankeji.exposedvertxsqlclient.sql.mapping.deleteWhere +import io.vertx.core.Verticle import io.vertx.core.Vertx -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import io.vertx.sqlclient.SqlClient import org.jetbrains.exposed.dao.id.IntIdTable +import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.exists import org.jetbrains.exposed.sql.selectAll object Examples : IntIdTable("examples") { @@ -18,19 +25,34 @@ object Examples : IntIdTable("examples") { val tables = arrayOf(Examples) +val evscConfig = ConnectionConfig.Socket("localhost", user = "user", password = "password", database = "database") + .toUniversalEvscConfig() + +object Alternative { + // Unix domain socket alternative + val evscConfig = defaultPostgresqlLocalConnectionConfig( + user = "user", + socketConnectionPassword = "password", + database = "database" + ).toPerformantUnixEvscConfig() +} + @OptIn(ExperimentalEvscApi::class) suspend fun examples(vertx: Vertx) { - val socketConnectionConfig = - ConnectionConfig.Socket("localhost", user = "user", password = "password", database = "database") - val exposedDatabase = exposedDatabaseConnectPostgreSql(socketConnectionConfig) - val databaseClient = createPgPoolDatabaseClient( - vertx, socketConnectionConfig, exposedDatabase = exposedDatabase - ) - - withContext(Dispatchers.IO) { - databaseClient.exposedTransaction { - SchemaUtils.create(*tables) - } + /** It may be more efficient to reuse a single shared [Database] to generate SQLs in multiple [DatabaseClient]s for [SqlClient]s in respective [Verticle]s. */ + val exposedDatabase = evscConfig.exposedConnectionConfig.exposedDatabaseConnectPostgresql() + + val sqlClient = createPgClient(vertx, evscConfig.vertxSqlClientConnectionConfig) + val pool = createPgPool(vertx, evscConfig.vertxSqlClientConnectionConfig) + val sqlConnection = createPgClient(vertx, evscConfig.vertxSqlClientConnectionConfig) + + val vertxSqlClient = sqlClient + + val databaseClient = DatabaseClient(vertxSqlClient, exposedDatabase) + + // put in `Vertx.executeBlocking` or `Dispatchers.IO` if needed + databaseClient.exposedTransaction { + SchemaUtils.create(*tables) } run { @@ -48,11 +70,11 @@ suspend fun examples(vertx: Vertx) { databaseClient.executeUpdate(Examples.updateStatement({ Examples.id eq 1 }) { it[name] = "AA" }) assert(updateRowCount == 1) - // The Exposed `Table` extension function `select` doesn't execute eagerly so it can be used directly. - val exampleName = databaseClient.executeQuery(Examples.select(Examples.name).where(Examples.id eq 1)) + // The Exposed `Table` extension function `select` doesn't execute eagerly so it can also be used directly. + val exampleName = databaseClient.executeQuery(Examples.selectStatement(Examples.name).where(Examples.id eq 1)) .single()[Examples.name] - databaseClient.executeSingleUpdate(Examples.deleteWhereStatement { Examples.id eq 1 }) // The function `deleteWhereStatement` still depends on the old DSL and will be updated. + databaseClient.executeSingleUpdate(Examples.deleteWhereStatement { id eq 1 }) databaseClient.executeSingleUpdate(Examples.deleteIgnoreWhereStatement { id eq 2 }) } @@ -64,9 +86,10 @@ suspend fun examples(vertx: Vertx) { val exampleName1 = databaseClient.select(Examples) { select(Examples.name).where(Examples.id eq 1) }.single()[Examples.name] - // This function still depends on the old SELECT DSL and will be updated. val exampleName2 = - databaseClient.selectSingleColumn(Examples, Examples.name) { selectAll().where(Examples.id eq 2) }.single() + databaseClient.selectSingleColumn(Examples, Examples.name) { where(Examples.id eq 2) }.single() + + val examplesExist = databaseClient.selectExpression(exists(Examples.selectAll())) val deleteRowCount1 = databaseClient.deleteWhere(Examples) { id eq 1 } assert(deleteRowCount1 == 1) diff --git a/lib/src/test/kotlin/com/huanshankeji/exposedvertxsqlclient/MappingExamples.kt b/integrated/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/MappingExamples.kt similarity index 88% rename from lib/src/test/kotlin/com/huanshankeji/exposedvertxsqlclient/MappingExamples.kt rename to integrated/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/MappingExamples.kt index 4daca30..fff5da1 100644 --- a/lib/src/test/kotlin/com/huanshankeji/exposedvertxsqlclient/MappingExamples.kt +++ b/integrated/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/MappingExamples.kt @@ -2,12 +2,11 @@ package com.huanshankeji.exposedvertxsqlclient import com.huanshankeji.exposed.datamapping.classproperty.PropertyColumnMappingConfig import com.huanshankeji.exposed.datamapping.classproperty.reflectionBasedClassPropertyDataMapper -import com.huanshankeji.exposedvertxsqlclient.sql.mapping.insert -import com.huanshankeji.exposedvertxsqlclient.sql.mapping.select +import com.huanshankeji.exposedvertxsqlclient.sql.mapping.insertWithMapper +import com.huanshankeji.exposedvertxsqlclient.sql.mapping.selectWithMapper import io.vertx.sqlclient.Pool import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList -import org.jetbrains.exposed.sql.select // copied and adapted from https://github.com/huanshankeji/exposed-adt-mapping/blob/main/lib/src/test/kotlin/com/huanshankeji/exposed/datamapping/classproperty/Examples.kt // Update accordingly to keep the code consistent. @@ -84,18 +83,18 @@ object Mappers { suspend fun mappingExamples(databaseClient: DatabaseClient) { val directorId = 1 val director = Director(directorId, "George Lucas") - databaseClient.insert(Directors, director, Mappers.director) + databaseClient.insertWithMapper(Directors, director, Mappers.director) val episodeIFilmDetails = FilmDetails(1, "Star Wars: Episode I – The Phantom Menace", directorId) // insert without the ID since it's `AUTO_INCREMENT` - databaseClient.insert(Films, episodeIFilmDetails, Mappers.filmDetailsWithDirectorId) + databaseClient.insertWithMapper(Films, episodeIFilmDetails, Mappers.filmDetailsWithDirectorId) val filmId = 2 val episodeIIFilmDetails = FilmDetails(2, "Star Wars: Episode II – Attack of the Clones", directorId) val filmWithDirectorId = FilmWithDirectorId(filmId, episodeIIFilmDetails) - databaseClient.insert(Films, filmWithDirectorId, Mappers.filmWithDirectorId) // insert with the ID + databaseClient.insertWithMapper(Films, filmWithDirectorId, Mappers.filmWithDirectorId) // insert with the ID - val fullFilms = databaseClient.select(filmsLeftJoinDirectors, Mappers.fullFilm) { - select(Films.filmId inList listOf(1, 2)) // This API still depends on the old SELECT DSL and will be refactored. + val fullFilms = databaseClient.selectWithMapper(filmsLeftJoinDirectors, Mappers.fullFilm) { + where(Films.filmId inList listOf(1, 2)) } } diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts deleted file mode 100644 index baa06d7..0000000 --- a/lib/build.gradle.kts +++ /dev/null @@ -1,43 +0,0 @@ -plugins { - conventions - id("com.huanshankeji.benchmark.kotlinx-benchmark-jvm-conventions") -} - -dependencies { - api(commonDependencies.exposed.core()) // TODO: use `implementation` when possible - // TODO: remove the Exposed JDBC dependency and the PostgresSQL dependency when there is no need to to generate SQLs with an Exposed transaction - runtimeOnly(commonDependencies.exposed.module("jdbc")) - api(commonDependencies.kotlinCommon.exposed()) - implementation("com.huanshankeji:exposed-adt-mapping:${DependencyVersions.exposedAdtMapping}") - - with(commonDependencies.vertx) { - implementation(platformStackDepchain()) - api(moduleWithoutVersion("sql-client")) // TODO: use `implementation` when possible - implementation(moduleWithoutVersion("lang-kotlin")) - implementation(moduleWithoutVersion("lang-kotlin-coroutines")) - } - implementation(commonDependencies.kotlinCommon.vertx()) - - implementation(commonDependencies.kotlinCommon.core()) - implementation(commonDependencies.arrow.core()) - - implementation(commonDependencies.kotlinCommon.net()) -} - - -// for PostgreSQL -dependencies { - runtimeOnly(commonDependencies.postgreSql()) - implementation(commonDependencies.vertx.moduleWithoutVersion("pg-client")) -} - -afterEvaluate { -// for the benchmarks - dependencies { - with(commonDependencies.testContainers) { - "benchmarksImplementation"(platformBom()) - "benchmarksImplementation"(postgreSql) - } - "benchmarksImplementation"(commonDependencies.slf4j.simple()) - } -} \ No newline at end of file diff --git a/lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt b/lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt deleted file mode 100644 index 928295c..0000000 --- a/lib/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.huanshankeji.exposed.benchmark - -import com.huanshankeji.kotlinx.coroutine.awaitAny -import kotlinx.benchmark.Benchmark -import kotlinx.benchmark.Scope -import kotlinx.benchmark.State -import kotlinx.coroutines.* -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction -import org.jetbrains.exposed.sql.transactions.experimental.suspendedTransactionAsync -import org.jetbrains.exposed.sql.transactions.transaction -import kotlin.concurrent.thread - -@State(Scope.Benchmark) -class TransactionBenchmark : WithContainerizedDatabaseBenchmark() { - @Benchmark - fun transaction() { - transaction(database) {} - } - - companion object { - const val `10K` = 10_000 - } - - @Benchmark - fun _10KTransactions() { - repeat(`10K`) { transaction(database) {} } - } - - private suspend fun awaitAsync10KTransactions() = - coroutineScope { - List(`10K`) { async { transaction(database) {} } }.awaitAll() - } - - @Benchmark - fun singleThreadConcurrent10KTransactions() = runBlocking { - awaitAsync10KTransactions() - } - - - @Benchmark - fun multiThreadConcurrent10KTransactions() = runBlocking { - withContext(Dispatchers.Default) { - awaitAsync10KTransactions() - } - } - - - @Benchmark - fun _10KSuspendedTransactions() = runBlocking { - repeat(`10K`) { newSuspendedTransaction(db = database) {} } - } - - @Benchmark - fun _10KSuspendedTransactionAsyncs() = runBlocking { - List(`10K`) { suspendedTransactionAsync(db = database) {} }.awaitAny() - } - - @Benchmark - fun multiThreadMultiConnectionEach10KLocalTransactions() { - List(Runtime.getRuntime().availableProcessors()) { - thread { - val database = databaseConnect() - repeat(`10K`) { transaction(database) {} } - } - }.forEach { - it.join() - } - } -} \ No newline at end of file diff --git a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ExposedDatabases.kt b/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ExposedDatabases.kt deleted file mode 100644 index 85dd82a..0000000 --- a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/ExposedDatabases.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.huanshankeji.exposedvertxsqlclient - -import org.jetbrains.exposed.sql.Database - -fun exposedDatabaseConnectPostgreSql(socketConnectionConfig: ConnectionConfig.Socket) = - with(socketConnectionConfig) { - Database.connect( - "jdbc:postgresql://$host${port?.let { ":$it" } ?: ""}/$database", - "org.postgresql.Driver", - user = user, - password = password - ) - } diff --git a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/LocalConnectionConfig.kt b/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/LocalConnectionConfig.kt deleted file mode 100644 index dbfd61c..0000000 --- a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/LocalConnectionConfig.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.huanshankeji.exposedvertxsqlclient - -import com.huanshankeji.net.LOCALHOST - -// TODO: move to a separate package and consider adding a prefix word such as "default" or "conventional" as this class is not general enough -/** - * A kind of connection config that can produce both a [ConnectionConfig.Socket] and a [ConnectionConfig.UnixDomainSocketWithPeerAuthentication] - * to connect to a local database server. - */ -class LocalConnectionConfig(val database: String, val user: String, val socketConnectionPassword: String) { - companion object { - const val UNIX_DOMAIN_SOCKET_PATH = "/var/run/postgresql" - const val SOCKET_HOST = LOCALHOST - } - - val socketConnectionConfig = - ConnectionConfig.Socket(SOCKET_HOST, null, user, socketConnectionPassword, database) - - val unixDomainSocketWithPeerAuthenticationConnectionConfig = - ConnectionConfig.UnixDomainSocketWithPeerAuthentication(UNIX_DOMAIN_SOCKET_PATH, user, database) -} - -@ExperimentalEvscApi -fun LocalConnectionConfig.toPerformantUnixEvscConfig() = - EvscConfig(socketConnectionConfig, unixDomainSocketWithPeerAuthenticationConnectionConfig) - -@ExperimentalEvscApi -fun LocalConnectionConfig.toUniversalEvscConfig() = - EvscConfig(socketConnectionConfig, socketConnectionConfig) diff --git a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/VertxSqlClients.kt b/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/VertxSqlClients.kt deleted file mode 100644 index f8feac9..0000000 --- a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/VertxSqlClients.kt +++ /dev/null @@ -1,163 +0,0 @@ -package com.huanshankeji.exposedvertxsqlclient - -import com.huanshankeji.Untested -import io.vertx.core.Vertx -import io.vertx.kotlin.coroutines.await -import io.vertx.kotlin.sqlclient.poolOptionsOf -import io.vertx.pgclient.PgConnectOptions -import io.vertx.pgclient.PgConnection -import io.vertx.pgclient.PgPool -import io.vertx.sqlclient.Pool -import io.vertx.sqlclient.PoolOptions -import io.vertx.sqlclient.SqlClient -import io.vertx.sqlclient.SqlConnection -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -@PublishedApi -internal inline fun createPgConnectOptions( - mainPgConnectOptions: PgConnectOptions.() -> Unit = {}, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, -): PgConnectOptions = - PgConnectOptions().apply { - cachePreparedStatements = true // This improves performance greatly so it's enabled by default. - mainPgConnectOptions() - extraPgConnectOptions() - } - -/* -// An extracted common `create` argument for `PgConnection`, but a suspend function has an incompatible type. -private val pgConnectionConnect: suspend (Vertx?, PgConnectOptions, Nothing?) -> PgConnection = { vertx, pgConnectOptions, _ -> - PgConnection.connect(vertx, pgConnectOptions).await() -} -*/ - -suspend fun SqlConnection.executeSetRole(role: String) = - query("SET ROLE $role").execute().await() - -// TODO: use `ConnectionConfig` as the argument directly in all the following functions - -inline fun createSocketGenericPgClient( - vertx: Vertx?, - host: String, port: Int?, database: String, user: String, password: String, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptionsT, - create: (Vertx?, PgConnectOptions, PoolOptionsT) -> Client -): Client { - val pgConnectOptions = createPgConnectOptions({ - this.host = host - port?.let { this.port = it } - this.database = database - this.user = user - this.password = password - }, extraPgConnectOptions) - - return create(vertx, pgConnectOptions, poolOptions) -} - -fun createSocketPgSqlClient( - vertx: Vertx?, - host: String, port: Int?, database: String, user: String, password: String, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptions = poolOptionsOf() -): SqlClient = - createSocketGenericPgClient( - vertx, host, port, database, user, password, extraPgConnectOptions, poolOptions, PgPool::client - ) - -fun createSocketPgPool( - vertx: Vertx?, - host: String, port: Int?, database: String, user: String, password: String, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptions = poolOptionsOf() -): PgPool = - createSocketGenericPgClient( - vertx, host, port, database, user, password, extraPgConnectOptions, poolOptions, PgPool::pool - ) - -@Untested -suspend fun createSocketPgConnection( - vertx: Vertx?, - host: String, port: Int?, database: String, user: String, password: String, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {} -): PgConnection = - createSocketGenericPgClient( - vertx, host, port, database, user, password, extraPgConnectOptions, null - ) { vertx, pgConnectOptions, _ -> - PgConnection.connect(vertx, pgConnectOptions).await() - } - - -inline fun createPeerAuthenticationUnixDomainSocketGenericPgClient( - vertx: Vertx?, - unixDomainSocketPath: String, database: String, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptionsT, - create: (Vertx?, PgConnectOptions, PoolOptionsT) -> Client -): Client { - val pgConnectOptions = createPgConnectOptions( - { - host = unixDomainSocketPath - this.database = database - user = System.getProperty("user.name") - }, extraPgConnectOptions - ) - - return create(vertx, pgConnectOptions, poolOptions) -} - -fun createPeerAuthenticationUnixDomainSocketPgSqlClient( - vertx: Vertx?, - unixDomainSocketPath: String, database: String, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptions = poolOptionsOf() -): SqlClient = - createPeerAuthenticationUnixDomainSocketGenericPgClient( - vertx, unixDomainSocketPath, database, extraPgConnectOptions, poolOptions, PgPool::client - ) - -suspend fun createUnixDomainSocketPgSqlClientAndSetRole( - vertx: Vertx?, - host: String, database: String, role: String, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptions = poolOptionsOf() -): SqlClient = - createPeerAuthenticationUnixDomainSocketPgSqlClient( - vertx, host, database, extraPgConnectOptions, poolOptions - ).apply { - // Is this done for all connections? - query("SET ROLE $role").execute().await() - } - -fun createPeerAuthenticationUnixDomainSocketPgPool( - vertx: Vertx?, - unixDomainSocketPath: String, database: String, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptions = poolOptionsOf() -): PgPool = - createPeerAuthenticationUnixDomainSocketGenericPgClient( - vertx, unixDomainSocketPath, database, extraPgConnectOptions, poolOptions, PgPool::pool - ) - -fun createPeerAuthenticationUnixDomainSocketPgPoolAndSetRole( - vertx: Vertx?, - host: String, database: String, role: String, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, poolOptions: PoolOptions = poolOptionsOf() -): PgPool = - createPeerAuthenticationUnixDomainSocketPgPool(vertx, host, database, extraPgConnectOptions, poolOptions) - .connectHandler { - CoroutineScope(Dispatchers.Unconfined).launch { - // TODO: are exceptions handled? - it.executeSetRole(role) - /** @see Pool.connectHandler */ - it.close().await() - } - } - -@Untested -suspend fun createPeerAuthenticationUnixDomainSocketPgConnectionAndSetRole( - vertx: Vertx?, - host: String, database: String, role: String, - extraPgConnectOptions: PgConnectOptions.() -> Unit = {} -): PgConnection = - createPeerAuthenticationUnixDomainSocketGenericPgClient( - vertx, host, database, extraPgConnectOptions, null - ) { vertx, pgConnectOptions, _ -> - PgConnection.connect(vertx, pgConnectOptions).await().apply { - executeSetRole(role) - } - } diff --git a/postgresql/api/exposed-vertx-sql-client-postgresql.api b/postgresql/api/exposed-vertx-sql-client-postgresql.api new file mode 100644 index 0000000..4926d01 --- /dev/null +++ b/postgresql/api/exposed-vertx-sql-client-postgresql.api @@ -0,0 +1,27 @@ +public final class com/huanshankeji/exposedvertxsqlclient/postgresql/DatabaseClientKt { + public static final fun withPgTransaction (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class com/huanshankeji/exposedvertxsqlclient/postgresql/exposed/ExposedDatabasesKt { + public static final fun exposedDatabaseConnectPostgresql (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; + public static synthetic fun exposedDatabaseConnectPostgresql$default (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; + public static final fun exposedDatabaseConnectPostgresqlWithParameterConnectionConfig (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; + public static synthetic fun exposedDatabaseConnectPostgresqlWithParameterConnectionConfig$default (Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig$Socket;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; +} + +public final class com/huanshankeji/exposedvertxsqlclient/postgresql/local/LocalConnectionConfigKt { + public static final field DEFAULT_POSTGRESQL_UNIX_DOMAIN_SOCKET_PATH Ljava/lang/String; + public static final fun defaultPostgresqlLocalConnectionConfig (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/huanshankeji/exposedvertxsqlclient/local/LocalConnectionConfig; + public static synthetic fun defaultPostgresqlLocalConnectionConfig$default (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/huanshankeji/exposedvertxsqlclient/local/LocalConnectionConfig; +} + +public final class com/huanshankeji/exposedvertxsqlclient/postgresql/vertx/pgclient/PgClientsKt { + public static final fun createGenericPgClientWithBuilder (Lio/vertx/core/Vertx;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lio/vertx/sqlclient/ClientBuilder;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lio/vertx/sqlclient/SqlClient; + public static final fun createPgClient (Lio/vertx/core/Vertx;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lio/vertx/sqlclient/SqlClient; + public static synthetic fun createPgClient$default (Lio/vertx/core/Vertx;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/vertx/sqlclient/SqlClient; + public static final fun createPgConnection (Lio/vertx/core/Vertx;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun createPgConnection$default (Lio/vertx/core/Vertx;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun createPgPool (Lio/vertx/core/Vertx;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lio/vertx/sqlclient/Pool; + public static synthetic fun createPgPool$default (Lio/vertx/core/Vertx;Lcom/huanshankeji/exposedvertxsqlclient/ConnectionConfig;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lio/vertx/sqlclient/Pool; +} + diff --git a/postgresql/build.gradle.kts b/postgresql/build.gradle.kts new file mode 100644 index 0000000..cd05fd4 --- /dev/null +++ b/postgresql/build.gradle.kts @@ -0,0 +1,17 @@ +import com.huanshankeji.cpnProject + +plugins { + `lib-conventions` +} + +dependencies { + with(commonDependencies.vertx) { implementation(platformStackDepchain()) } // needed + implementation(cpnProject(project, ":core")) + + // TODO remove the Exposed JDBC dependency and the PostgresSQL dependency when there is no need to to generate SQLs with an Exposed transaction + runtimeOnly(commonDependencies.exposed.module("jdbc")) + runtimeOnly(commonDependencies.postgreSql()) + implementation(commonDependencies.vertx.moduleWithoutVersion("pg-client")) + implementation(commonDependencies.kotlinCommon.core()) // for `Untested` + //implementation(commonDependencies.kotlinCommon.vertx()) // for `PgPoolOptions.setUpConventionally` +} diff --git a/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/DatabaseClient.kt b/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/DatabaseClient.kt new file mode 100644 index 0000000..54ea5d2 --- /dev/null +++ b/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/DatabaseClient.kt @@ -0,0 +1,9 @@ +package com.huanshankeji.exposedvertxsqlclient.postgresql + +import com.huanshankeji.exposedvertxsqlclient.DatabaseClient +import com.huanshankeji.exposedvertxsqlclient.withTypedTransaction +import io.vertx.pgclient.PgConnection +import io.vertx.sqlclient.Pool + +suspend fun DatabaseClient.withPgTransaction(function: suspend (DatabaseClient) -> T): T = + withTypedTransaction(function) diff --git a/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/exposed/ExposedDatabases.kt b/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/exposed/ExposedDatabases.kt new file mode 100644 index 0000000..c17199d --- /dev/null +++ b/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/exposed/ExposedDatabases.kt @@ -0,0 +1,33 @@ +package com.huanshankeji.exposedvertxsqlclient.postgresql.exposed + +import com.huanshankeji.exposedvertxsqlclient.ConnectionConfig +import com.huanshankeji.exposedvertxsqlclient.ExperimentalEvscApi +import com.huanshankeji.exposedvertxsqlclient.exposed.exposedDatabaseConnect +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.DatabaseConfig +import org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManager +import org.jetbrains.exposed.sql.transactions.TransactionManager +import java.sql.Connection + +/** + * @see exposedDatabaseConnect + */ +@ExperimentalEvscApi +fun ConnectionConfig.Socket.exposedDatabaseConnectPostgresql( + setupConnection: (Connection) -> Unit = {}, + databaseConfig: DatabaseConfig? = null, + manager: (Database) -> TransactionManager = { ThreadLocalTransactionManager(it) } +) = + exposedDatabaseConnect( + "postgresql", "org.postgresql.Driver", setupConnection, databaseConfig, manager + ) + +@ExperimentalEvscApi +@JvmName("exposedDatabaseConnectPostgresqlWithParameterConnectionConfig") +fun exposedDatabaseConnectPostgresql( + socketConnectionConfig: ConnectionConfig.Socket, + setupConnection: (Connection) -> Unit = {}, + databaseConfig: DatabaseConfig? = null, + manager: (Database) -> TransactionManager = { ThreadLocalTransactionManager(it) } +) = + socketConnectionConfig.exposedDatabaseConnectPostgresql(setupConnection, databaseConfig, manager) diff --git a/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/local/LocalConnectionConfig.kt b/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/local/LocalConnectionConfig.kt new file mode 100644 index 0000000..6187485 --- /dev/null +++ b/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/local/LocalConnectionConfig.kt @@ -0,0 +1,16 @@ +package com.huanshankeji.exposedvertxsqlclient.postgresql.local + +import com.huanshankeji.exposedvertxsqlclient.ExperimentalEvscApi +import com.huanshankeji.exposedvertxsqlclient.local.LocalConnectionConfig + +// TODO consider moving to "kotlin-common" + +const val DEFAULT_POSTGRESQL_UNIX_DOMAIN_SOCKET_PATH = "/var/run/postgresql" + +@ExperimentalEvscApi +fun defaultPostgresqlLocalConnectionConfig( + socketConnectionPort: Int? = null, user: String, socketConnectionPassword: String, database: String +) = + LocalConnectionConfig( + socketConnectionPort, DEFAULT_POSTGRESQL_UNIX_DOMAIN_SOCKET_PATH, user, socketConnectionPassword, database + ) diff --git a/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/vertx/pgclient/PgClients.kt b/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/vertx/pgclient/PgClients.kt new file mode 100644 index 0000000..700f748 --- /dev/null +++ b/postgresql/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/postgresql/vertx/pgclient/PgClients.kt @@ -0,0 +1,97 @@ +@file:OptIn(ExperimentalEvscApi::class) + +package com.huanshankeji.exposedvertxsqlclient.postgresql.vertx.pgclient + +import com.huanshankeji.Untested +import com.huanshankeji.exposedvertxsqlclient.ConnectionConfig +import com.huanshankeji.exposedvertxsqlclient.ExperimentalEvscApi +import com.huanshankeji.exposedvertxsqlclient.vertx.sqlclient.CoConnectHandler +import com.huanshankeji.exposedvertxsqlclient.vertx.sqlclient.createGenericSqlClient +import com.huanshankeji.exposedvertxsqlclient.vertx.sqlclient.createGenericSqlClientWithBuilder +import com.huanshankeji.exposedvertxsqlclient.vertx.sqlclient.createGenericSqlConnection +import io.vertx.core.Vertx +import io.vertx.pgclient.PgBuilder +import io.vertx.pgclient.PgConnectOptions +import io.vertx.pgclient.PgConnection +import io.vertx.pgclient.impl.PgPoolOptions +import io.vertx.sqlclient.ClientBuilder +import io.vertx.sqlclient.Pool +import io.vertx.sqlclient.SqlClient + +/** + * @see createGenericSqlClient + */ +// made not inline anymore for easier debugging +@ExperimentalEvscApi +fun > createGenericPgClientWithBuilder( + vertx: Vertx?, + connectionConfig: ConnectionConfig, + clientBuilder: ClientBuilderT, + extraPgConnectOptions: PgConnectOptions.() -> Unit, + extraPgPoolOptions: PgPoolOptions.() -> Unit, + connectHandlerExtra: CoConnectHandler +): SqlClientT = + createGenericSqlClientWithBuilder( + vertx, + connectionConfig, + clientBuilder, + PgConnectOptions(), + extraPgConnectOptions, + extraPgPoolOptions, + connectHandlerExtra, + PgPoolOptions() + ) + +fun createPgClient( + vertx: Vertx?, + connectionConfig: ConnectionConfig, + extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, + extraPoolOptions: PgPoolOptions.() -> Unit = {}, + connectHandlerExtra: CoConnectHandler = null, +): SqlClient = + createGenericPgClientWithBuilder( + vertx, + connectionConfig, + PgBuilder.client(), + extraPgConnectOptions, + extraPoolOptions, + connectHandlerExtra + ) + +/** + * @see createGenericSqlClient + */ +fun createPgPool( + vertx: Vertx?, + connectionConfig: ConnectionConfig, + extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, + extraPoolOptions: PgPoolOptions.() -> Unit = {}, + connectHandlerExtra: CoConnectHandler = null, +): Pool = + createGenericPgClientWithBuilder( + vertx, + connectionConfig, + PgBuilder.pool(), + extraPgConnectOptions, + extraPoolOptions, + connectHandlerExtra + ) + +/** + * @see createGenericSqlClient + */ +@Untested +suspend fun createPgConnection( + vertx: Vertx?, + connectionConfig: ConnectionConfig, + extraPgConnectOptions: PgConnectOptions.() -> Unit = {}, + connectHandlerExtra: CoConnectHandler = null +): PgConnection = + createGenericSqlConnection( + vertx, + connectionConfig, + PgConnection::connect, + PgConnectOptions(), + extraPgConnectOptions, + connectHandlerExtra + ) diff --git a/settings.gradle.kts b/settings.gradle.kts index f25580d..3b86ea5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,22 @@ rootProject.name = "exposed-vertx-sql-client" -include("lib") -project(":lib").name = rootProject.name + "-postgresql" + +include("core") +include("sql-dsl") +include("sql-dsl-with-mapper") +include("postgresql") +include("integrated") + +fun ProjectDescriptor.setProjectConcatenatedNames(prefix: String) { + name = prefix + name + for (child in children) + child.setProjectConcatenatedNames("$name-") +} +rootProject.setProjectConcatenatedNames("") + +// for Dokka +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + } +} diff --git a/sql-dsl-with-mapper/api/exposed-vertx-sql-client-sql-dsl-with-mapper.api b/sql-dsl-with-mapper/api/exposed-vertx-sql-client-sql-dsl-with-mapper.api new file mode 100644 index 0000000..89457fe --- /dev/null +++ b/sql-dsl-with-mapper/api/exposed-vertx-sql-client-sql-dsl-with-mapper.api @@ -0,0 +1,18 @@ +public final class com/huanshankeji/exposedvertxsqlclient/sql/mapping/DatabaseClientSqlWithMapperKt { + public static final fun batchInsertIgnoreWithMapper (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Iterable;Lcom/huanshankeji/exposed/datamapping/DataUpdateMapper;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun batchInsertWithMapper (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Iterable;Lcom/huanshankeji/exposed/datamapping/DataUpdateMapper;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun batchUpdateBuilderSetter (Lcom/huanshankeji/exposed/datamapping/DataUpdateMapper;)Lkotlin/jvm/functions/Function3; + public static final fun executeQueryWithMapper (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Query;Lcom/huanshankeji/exposed/datamapping/DataQueryMapper;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun executeVertxSqlClientRowQueryWithMapper (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Query;Lcom/huanshankeji/vertx/sqlclient/datamapping/RowDataQueryMapper;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun insertIgnoreWithMapper (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Object;Lcom/huanshankeji/exposed/datamapping/DataUpdateMapper;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun insertWithMapper (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Object;Lcom/huanshankeji/exposed/datamapping/DataUpdateMapper;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun selectWithMapper (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/ColumnSet;Lcom/huanshankeji/exposed/datamapping/DataQueryMapper;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun selectWithMapper (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/ColumnSet;Lcom/huanshankeji/exposed/datamapping/DataQueryMapper;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun updateWithMapper (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Ljava/lang/Object;Lcom/huanshankeji/exposed/datamapping/DataUpdateMapper;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun updateWithMapper$default (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Ljava/lang/Object;Lcom/huanshankeji/exposed/datamapping/DataUpdateMapper;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; +} + +public abstract interface class com/huanshankeji/vertx/sqlclient/datamapping/RowDataQueryMapper { + public abstract fun rowToData (Lio/vertx/sqlclient/Row;)Ljava/lang/Object; +} + diff --git a/sql-dsl-with-mapper/build.gradle.kts b/sql-dsl-with-mapper/build.gradle.kts new file mode 100644 index 0000000..92d9e90 --- /dev/null +++ b/sql-dsl-with-mapper/build.gradle.kts @@ -0,0 +1,13 @@ +import com.huanshankeji.cpnProject + +plugins { + `lib-conventions` +} + +dependencies { + with(commonDependencies.vertx) { implementation(platformStackDepchain()) } // needed + implementation(cpnProject(project, ":core")) + implementation(cpnProject(project, ":sql-dsl")) + + implementation("com.huanshankeji:exposed-adt-mapping:${DependencyVersions.exposedAdtMapping}") // for `updateBuilderSetter`, `DataQueryMapper` and `DataUpdateMapper` +} diff --git a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/classpropertymapping/ClassPropertyMapping.kt b/sql-dsl-with-mapper/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/classpropertymapping/ClassPropertyMapping.kt similarity index 73% rename from lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/classpropertymapping/ClassPropertyMapping.kt rename to sql-dsl-with-mapper/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/classpropertymapping/ClassPropertyMapping.kt index 784ede4..64b4d63 100644 --- a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/classpropertymapping/ClassPropertyMapping.kt +++ b/sql-dsl-with-mapper/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/classpropertymapping/ClassPropertyMapping.kt @@ -6,18 +6,20 @@ import com.huanshankeji.vertx.sqlclient.datamapping.RowDataQueryMapper import io.vertx.sqlclient.Row import kotlin.reflect.KClass +// TODO all definitions are made private because they are not complete yet + /** * @see ClassPropertyColumnMappings */ // since Kotlin 2.0.0: "'Nothing' property type can't be specified with type alias." -typealias ClassPropertyColumnIndexMappings = Unit // TODO +private typealias ClassPropertyColumnIndexMappings = Unit // TODO -typealias VertxSqlClientRowDataQueryMapper = RowDataQueryMapper +private typealias VertxSqlClientRowDataQueryMapper = RowDataQueryMapper /** * @see ReflectionBasedClassPropertyDataMapper */ -class ReflectionBasedClassPropertyIndexVertxSqlClientRowDataQueryMapper( +private class ReflectionBasedClassPropertyIndexVertxSqlClientRowDataQueryMapper( val clazz: KClass, val classPropertyColumnIndexMappings: ClassPropertyColumnIndexMappings ) : RowDataQueryMapper { diff --git a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/mapping/DatabaseClientSqlWithMapper.kt b/sql-dsl-with-mapper/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/mapping/DatabaseClientSqlWithMapper.kt similarity index 66% rename from lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/mapping/DatabaseClientSqlWithMapper.kt rename to sql-dsl-with-mapper/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/mapping/DatabaseClientSqlWithMapper.kt index c6319e5..b45c68e 100644 --- a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/mapping/DatabaseClientSqlWithMapper.kt +++ b/sql-dsl-with-mapper/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/mapping/DatabaseClientSqlWithMapper.kt @@ -4,46 +4,51 @@ import com.huanshankeji.exposed.BuildWhere import com.huanshankeji.exposed.datamapping.DataQueryMapper import com.huanshankeji.exposed.datamapping.DataUpdateMapper import com.huanshankeji.exposed.datamapping.updateBuilderSetter -import com.huanshankeji.exposed.deleteIgnoreWhereStatement -import com.huanshankeji.exposed.deleteWhereStatement import com.huanshankeji.exposedvertxsqlclient.DatabaseClient import com.huanshankeji.exposedvertxsqlclient.ExperimentalEvscApi import com.huanshankeji.exposedvertxsqlclient.sql.* import com.huanshankeji.vertx.sqlclient.datamapping.RowDataQueryMapper import io.vertx.sqlclient.RowSet -import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.ColumnSet +import org.jetbrains.exposed.sql.Query +import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.statements.UpdateBuilder // TODO move to a separate module -// TODO Note that using these DSLs reduces the composability of statements, for example, when moving a query into a subquery. (this statement can be moved into docs some day) @ExperimentalEvscApi -suspend fun DatabaseClient<*>.executeQuery( +suspend fun DatabaseClient<*>.executeQueryWithMapper( query: Query, dataQueryMapper: DataQueryMapper ): RowSet = executeWithMapping(query) { row -> dataQueryMapper.resultRowToData(row.toExposedResultRowWithTransaction(query)) } @ExperimentalEvscApi -suspend fun DatabaseClient<*>.executeVertxSqlClientRowQuery( +suspend fun DatabaseClient<*>.executeVertxSqlClientRowQueryWithMapper( query: Query, rowDataQueryMapper: RowDataQueryMapper ): RowSet = executeWithMapping(query, rowDataQueryMapper::rowToData) @ExperimentalEvscApi -suspend fun DatabaseClient<*>.select( - columnSet: ColumnSet, dataQueryMapper: DataQueryMapper, buildQuery: FieldSet.() -> Query +suspend fun DatabaseClient<*>.selectWithMapper( + columnSet: ColumnSet, dataQueryMapper: DataQueryMapper, buildQuery: Query.() -> Query ) = - executeQuery(columnSet.slice(dataQueryMapper.neededColumns).buildQuery(), dataQueryMapper) + executeQueryWithMapper(columnSet.select(dataQueryMapper.neededColumns).buildQuery(), dataQueryMapper) @ExperimentalEvscApi -suspend fun DatabaseClient<*>.insert( +suspend fun DatabaseClient<*>.selectWithMapper( + columnSet: ColumnSet, dataQueryMapper: DataQueryMapper +) = + selectWithMapper(columnSet, dataQueryMapper) { this } + +@ExperimentalEvscApi +suspend fun DatabaseClient<*>.insertWithMapper( table: Table, data: Data, dataUpdateMapper: DataUpdateMapper ) = insert(table, dataUpdateMapper.updateBuilderSetter(data)) @ExperimentalEvscApi -suspend fun DatabaseClient<*>.insertIgnore( +suspend fun DatabaseClient<*>.insertIgnoreWithMapper( table: Table, data: Data, dataUpdateMapper: DataUpdateMapper ) = insertIgnore(table, dataUpdateMapper.updateBuilderSetter(data)) @@ -56,13 +61,13 @@ fun DataUpdateMapper.batchUpdateBuild // TODO: consider removing the table param by adding it to `DataUpdateMapper` @ExperimentalEvscApi -suspend fun DatabaseClient<*>.batchInsert( +suspend fun DatabaseClient<*>.batchInsertWithMapper( table: Table, data: Iterable, dataUpdateMapper: DataUpdateMapper ) = batchInsert(table, data, dataUpdateMapper.batchUpdateBuilderSetter()) @ExperimentalEvscApi -suspend fun DatabaseClient<*>.batchInsertIgnore( +suspend fun DatabaseClient<*>.batchInsertIgnoreWithMapper( table: Table, data: Iterable, dataUpdateMapper: DataUpdateMapper ) = batchInsertIgnore(table, data, dataUpdateMapper.batchUpdateBuilderSetter()) @@ -72,17 +77,7 @@ suspend fun DatabaseClient<*>.batchInsertIgnore( * In most cases you should specify the fields to update in a more detailed way instead of using this function. */ @ExperimentalEvscApi -suspend fun DatabaseClient<*>.update( +suspend fun DatabaseClient<*>.updateWithMapper( table: Table, where: BuildWhere? = null, limit: Int? = null, data: Data, dataUpdateMapper: DataUpdateMapper ) = update(table, where, limit, dataUpdateMapper.updateBuilderSetter(data)) - -suspend fun DatabaseClient<*>.deleteWhere( - table: T, limit: Int? = null, offset: Long? = null, op: T.(ISqlExpressionBuilder) -> Op -) = - executeUpdate(table.deleteWhereStatement(limit, offset, op)) - -suspend fun DatabaseClient<*>.deleteIgnoreWhere( - table: T, limit: Int? = null, offset: Long? = null, op: T.(ISqlExpressionBuilder) -> Op -) = - executeUpdate(table.deleteIgnoreWhereStatement(limit, offset, op)) diff --git a/lib/src/main/kotlin/com/huanshankeji/vertx/sqlclient/datamapping/RowDataQueryMapper.kt b/sql-dsl-with-mapper/src/main/kotlin/com/huanshankeji/vertx/sqlclient/datamapping/RowDataQueryMapper.kt similarity index 100% rename from lib/src/main/kotlin/com/huanshankeji/vertx/sqlclient/datamapping/RowDataQueryMapper.kt rename to sql-dsl-with-mapper/src/main/kotlin/com/huanshankeji/vertx/sqlclient/datamapping/RowDataQueryMapper.kt diff --git a/sql-dsl/api/exposed-vertx-sql-client-sql-dsl.api b/sql-dsl/api/exposed-vertx-sql-client-sql-dsl.api new file mode 100644 index 0000000..b6a5654 --- /dev/null +++ b/sql-dsl/api/exposed-vertx-sql-client-sql-dsl.api @@ -0,0 +1,35 @@ +public final class com/huanshankeji/exposedvertxsqlclient/sql/DatabaseClientSqlKt { + public static final fun batchInsert (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun batchInsertIgnore (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun batchInsertSelect (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Ljava/lang/Iterable;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun batchSingleOrNoUpdate (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun batchSingleOrNoUpdate$default (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun batchUpdate (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun batchUpdate$default (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun deleteIgnoreWhere (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Integer;Ljava/lang/Long;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun deleteIgnoreWhere$default (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Integer;Ljava/lang/Long;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun deleteWhere (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Integer;Ljava/lang/Long;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun deleteWhere$default (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Integer;Ljava/lang/Long;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun executeInsertIgnore (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun insert (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun insertIgnore (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun insertIgnoreSelect (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lorg/jetbrains/exposed/sql/AbstractQuery;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun insertIgnoreSelect$default (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lorg/jetbrains/exposed/sql/AbstractQuery;Ljava/util/List;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun insertIgnoreSingle (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun insertSelect (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lorg/jetbrains/exposed/sql/AbstractQuery;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun insertSelect$default (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lorg/jetbrains/exposed/sql/AbstractQuery;Ljava/util/List;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun insertSingle (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun select (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/ColumnSet;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun select (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/ColumnSet;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun selectBatch (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/FieldSet;Lkotlin/jvm/functions/Function2;Ljava/lang/Iterable;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun selectColumnSetExpression (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/ColumnSet;Lorg/jetbrains/exposed/sql/Expression;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun selectExpression (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lkotlin/reflect/KClass;Lorg/jetbrains/exposed/sql/Expression;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun selectSingleColumn (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/ColumnSet;Lorg/jetbrains/exposed/sql/Column;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun selectSingleColumn (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/ColumnSet;Lorg/jetbrains/exposed/sql/Column;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun selectSingleEntityIdColumn (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/ColumnSet;Lorg/jetbrains/exposed/sql/Column;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun sortDataAndBatchUpdate (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sortDataAndBatchUpdate$default (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun update (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun update$default (Lcom/huanshankeji/exposedvertxsqlclient/DatabaseClient;Lorg/jetbrains/exposed/sql/Table;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; +} + diff --git a/sql-dsl/build.gradle.kts b/sql-dsl/build.gradle.kts new file mode 100644 index 0000000..febf3bb --- /dev/null +++ b/sql-dsl/build.gradle.kts @@ -0,0 +1,12 @@ +import com.huanshankeji.cpnProject + +plugins { + `lib-conventions` +} + +dependencies { + with(commonDependencies.vertx) { implementation(platformStackDepchain()) } // needed + implementation(cpnProject(project, ":core")) + + compileOnly(commonDependencies.kotlinCommon.vertx()) // for `sortDataAndExecuteBatch` +} diff --git a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/DatabaseClientSql.kt b/sql-dsl/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/DatabaseClientSql.kt similarity index 81% rename from lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/DatabaseClientSql.kt rename to sql-dsl/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/DatabaseClientSql.kt index 4d0741a..2b47c26 100644 --- a/lib/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/DatabaseClientSql.kt +++ b/sql-dsl/src/main/kotlin/com/huanshankeji/exposedvertxsqlclient/sql/DatabaseClientSql.kt @@ -25,7 +25,6 @@ suspend inline fun DatabaseClient<*>.select( ): RowSet = executeQuery(columnSet.buildQuery(), resultRowMapper) -// TODO adapt to the new SELECT DSL or deprecate suspend inline fun DatabaseClient<*>.select( columnSet: ColumnSet, buildQuery: ColumnSet.() -> Query ): RowSet = @@ -33,50 +32,33 @@ suspend inline fun DatabaseClient<*>.select( select(columnSet, buildQuery, { this }) -// TODO adapt to the new SELECT DSL or deprecate /** * SQL: `SELECT FROM ;`. * Examples: `SELECT COUNT(*) FROM
;`, `SELECT SUM() FROM
;`. */ @ExperimentalEvscApi -suspend fun DatabaseClient<*>.selectTableExpression( - columnSet: ColumnSet, expression: Expression, buildQuery: FieldSet.() -> Query +suspend fun DatabaseClient<*>.selectColumnSetExpression( + columnSet: ColumnSet, expression: Expression, buildQuery: Query.() -> Query ): RowSet = - select(columnSet, { slice(expression).buildQuery() }, { this[expression] }) + select(columnSet, { select(expression).buildQuery() }, { this[expression] }) // This function with `mapper` is not really useful @ExperimentalEvscApi suspend inline fun DatabaseClient<*>.selectSingleColumn( columnSet: ColumnSet, column: Column, - buildQuery: FieldSet.() -> Query, + buildQuery: Query.() -> Query, crossinline mapper: ColumnT.() -> DataT ): RowSet = - select(columnSet, { slice(column).buildQuery() }, { this[column].mapper() }) + select(columnSet, { select(column).buildQuery() }, { this[column].mapper() }) - -@Deprecated("Use `selectSingleColumn`.", ReplaceWith("selectSingleColumn(columnSet, column, buildQuery, mapper)")) -@ExperimentalEvscApi -suspend inline fun DatabaseClient<*>.executeSingleColumnSelectQuery( - columnSet: ColumnSet, column: Column, buildQuery: FieldSet.() -> Query, crossinline mapper: T.() -> R -): RowSet = - selectSingleColumn(columnSet, column, buildQuery, mapper) - -// TODO adapt to the new SELECT DSL or deprecate suspend fun DatabaseClient<*>.selectSingleColumn( - columnSet: ColumnSet, column: Column, buildQuery: FieldSet.() -> Query -): RowSet = - selectTableExpression(columnSet, column, buildQuery) - -@Deprecated("Use `selectSingleColumn`.", ReplaceWith("selectSingleColumn(columnSet, column, buildQuery)")) -suspend fun DatabaseClient<*>.executeSingleColumnSelectQuery( - columnSet: ColumnSet, column: Column, buildQuery: FieldSet.() -> Query + columnSet: ColumnSet, column: Column, buildQuery: Query.() -> Query ): RowSet = - selectSingleColumn(columnSet, column, buildQuery) + selectColumnSetExpression(columnSet, column, buildQuery) -// TODO adapt to the new SELECT DSL or deprecate suspend fun > DatabaseClient<*>.selectSingleEntityIdColumn( - columnSet: ColumnSet, column: Column>, buildQuery: FieldSet.() -> Query + columnSet: ColumnSet, column: Column>, buildQuery: Query.() -> Query ): RowSet = selectSingleColumn(columnSet, column, buildQuery) { value } @@ -87,12 +69,12 @@ suspend fun > DatabaseClient<*>.selectSingleEntityIdColumn( */ // see: https://github.com/JetBrains/Exposed/issues/621 suspend fun DatabaseClient<*>.selectExpression(clazz: KClass, expression: Expression): T? = - executeForVertxSqlClientRowSet(Table.Dual.slice(expression).selectAll()) + executeForVertxSqlClientRowSet(Table.Dual.select(expression)) .single()[clazz.java, 0] suspend inline fun DatabaseClient<*>.selectExpression(expression: Expression): T = @Suppress("UNCHECKED_CAST") - (selectExpression(T::class as KClass, expression as Expression)) as T + selectExpression(T::class as KClass, expression as Expression) as T @ExperimentalEvscApi suspend fun DatabaseClient<*>.insert(table: T, body: T.(InsertStatement) -> Unit) = @@ -152,7 +134,9 @@ suspend fun DatabaseClient<*>.selectBatch( ): Sequence> = executeBatchQuery(fieldSet, data.asSequence().map { fieldSet.buildQuery(it) }.asIterable()) - +/** + * @see DatabaseClient.executeBatchUpdate + */ suspend fun DatabaseClient<*>.batchInsert( table: T, data: Iterable, body: T.(InsertStatement, E) -> Unit ) = @@ -161,6 +145,9 @@ suspend fun DatabaseClient<*>.batchInsert( }.asIterable()) .forEach { dbAssert(it == 1) } +/** + * @see DatabaseClient.executeBatchUpdate + */ suspend fun DatabaseClient<*>.batchInsertIgnore( table: T, data: Iterable, body: T.(InsertStatement, E) -> Unit ) = @@ -172,6 +159,7 @@ suspend fun DatabaseClient<*>.batchInsertIgnore( /** * This function is not conventional and it usages are likely to degrade performance. + * @see DatabaseClient.executeBatchUpdate */ @ExperimentalEvscApi suspend fun DatabaseClient<*>.batchInsertSelect( @@ -179,6 +167,10 @@ suspend fun DatabaseClient<*>.batchInsertSelect( ) = executeBatchUpdate(statements) +/** + * @see DatabaseClient.executeBatchUpdate + * @see sortDataAndBatchUpdate + */ suspend fun DatabaseClient<*>.batchUpdate( table: T, data: Iterable, where: BuildWhere? = null, limit: Int? = null, body: T.(UpdateStatement, E) -> Unit ) = @@ -188,6 +180,7 @@ suspend fun DatabaseClient<*>.batchUpdate( /** * @return a sequence indicating whether each update statement is updated in the batch. + * @see batchUpdate */ suspend fun DatabaseClient<*>.batchSingleOrNoUpdate( table: T, data: Iterable, where: BuildWhere? = null, limit: Int? = null, body: T.(UpdateStatement, E) -> Unit @@ -196,6 +189,7 @@ suspend fun DatabaseClient<*>.batchSingleOrNoUpdate( /** * @see sortDataAndExecuteBatch + * @see batchUpdate */ suspend fun > DatabaseClient<*>.sortDataAndBatchUpdate( table: T, @@ -203,3 +197,14 @@ suspend fun > Databa where: BuildWhere? = null, limit: Int? = null, body: T.(UpdateStatement, E) -> Unit ) = batchUpdate(table, data.sortedBy(selector), where, limit, body) + + +suspend fun DatabaseClient<*>.deleteWhere( + table: T, limit: Int? = null, offset: Long? = null, op: TableAwareWithSqlExpressionBuilderBuildWhere +) = + executeUpdate(table.deleteWhereStatement(limit, offset, op)) + +suspend fun DatabaseClient<*>.deleteIgnoreWhere( + table: T, limit: Int? = null, offset: Long? = null, op: TableAwareWithSqlExpressionBuilderBuildWhere +) = + executeUpdate(table.deleteIgnoreWhereStatement(limit, offset, op))