├── .github └── workflows │ └── CI.yml ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── LICENSE.txt ├── README.md ├── THIRD_PARTY.txt ├── codecov.yml ├── dco.txt ├── license-header.txt ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main └── java │ └── org │ └── qstd │ ├── ColumnMappingPart.java │ ├── ColumnNamesComparator.java │ ├── ColumnNamesExtractor.java │ ├── ColumnOrdersFinder.java │ ├── ColumnValueFormatter.java │ ├── ColumnsMapping.java │ ├── ColumnsMappingGroup.java │ ├── ColumnsMappingsFinder.java │ ├── DatabaseMetadataFinder.java │ ├── DatasetRow.java │ ├── DatasetRowComparatorBuilder.java │ ├── DatasetRowSet.java │ ├── DatasetRowsFinder.java │ ├── DatasetRowsGenerator.java │ ├── DeleteToSelectTransformer.java │ ├── InsertStatementsGenerator.java │ ├── MissingNotNullColumnsFinder.java │ ├── NotNullColumnsFinder.java │ ├── PreparedStatementBuilder.java │ ├── PrimaryKeyColumnsFinder.java │ ├── QuickSqlTestData.java │ ├── ReferencedTable.java │ ├── ReferencedTableSet.java │ ├── ReferencedTablesFinder.java │ ├── RowFinder.java │ ├── SelectTransformer.java │ ├── SelectTransformerFactory.java │ ├── SqlQuery.java │ ├── UpdateToSelectTransformer.java │ └── dbtype │ ├── BaseColumnOrdersFinder.java │ ├── BaseColumnsMappingsFinder.java │ ├── BaseNotNullColumnsFinder.java │ ├── BasePrimaryKeyColumnsFinder.java │ ├── BaseReferencedTablesFinder.java │ ├── DatabaseMetadataFinderFactory.java │ ├── DatabaseMetadataFinderWithCache.java │ ├── DatabaseType.java │ ├── DatabaseUrlFinder.java │ ├── DefaultColumnOrdersFinder.java │ ├── DefaultDatabaseMetadataFinder.java │ ├── DefaultNotNullColumnsFinder.java │ ├── DefaultPrimaryKeyColumnsFinder.java │ ├── H2MetadataFinder.java │ ├── HsqlDbMetadataFinder.java │ ├── MSSQLServerMetadataFinder.java │ ├── MariaDBMySQLMetadataFinder.java │ ├── OracleMetadataFinder.java │ ├── PostgreSqlMariaDbReferencedTablesFinder.java │ └── PostgreSqlMetadataFinder.java └── test ├── java └── org │ └── qstd │ └── test │ ├── DataSourceBuilder.java │ ├── DatasetRowApiTest.java │ ├── DatasetRowsMergingTest.java │ ├── DeleteTest.java │ ├── FastTestSuite.java │ ├── H2Config.java │ ├── H2DateTypesTest.java │ ├── H2Test.java │ ├── HsqlDbTest.java │ ├── InsertTest.java │ ├── JdbcRoundtripTest.java │ ├── MSSQLServerTest.java │ ├── MariaDBSlowTest.java │ ├── MariaDBTest.java │ ├── NotFullyManagedDatabaseTest.java │ ├── OracleTest.java │ ├── PostgreSqlTest.java │ ├── SelectTest.java │ ├── SortInsertStatementsTest.java │ ├── SortInsertStatementsWithPkTest.java │ ├── SqlExecutor.java │ ├── TestTable.java │ └── UpdateTest.java └── resources ├── junit-platform.properties └── logback-test.xml /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | linux: 7 | runs-on: ubuntu-latest 8 | name: Java ${{ matrix.java }} 9 | strategy: 10 | matrix: 11 | java: [8, 11] 12 | env: 13 | REPO_SLUG: ${{ github.repository }} 14 | BRANCH: ${{ github.head_ref }} 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up JDK ${{ matrix.java }} 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: ${{ matrix.java }} 21 | - name: Cache Maven dependencies 22 | uses: actions/cache@v2 23 | with: 24 | path: ~/.m2 25 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} 26 | restore-keys: ${{ runner.os }}-m2 27 | - name: Execute tests 28 | run: mvn verify 29 | - name: check license header is present in all files 30 | run: mvn license:check 31 | - name: Upload coverage to Codecov 32 | run: bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Maven 2 | target 3 | 4 | # IntelliJ 5 | .idea 6 | *.iml 7 | out 8 | 9 | # Eclipse 10 | .classpath 11 | .project 12 | .settings 13 | 14 | # NetBeans 15 | .nbattrs 16 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quick-perf/quick-sql-test-data/4e6439f0f4c54b70d3166c7bd2bf805646967876/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quick SQL test data 2 | 3 | 4 | Maven Central 6 | 7 |    8 | 9 | License 11 | 12 |    13 | 14 | Build Status 16 | 17 |   18 | 19 | 20 | 21 | 22 | ## Why use *Quick SQL test data*? 23 | Writing datasets with SQL may be tedious and time-consuming because of database integrity constraints. 24 | 25 | *This Java library aims to ease the generation of datasets to test SQL queries. It produces INSERT statements taking account of integrity constraints.* 26 | 27 | The library automatically: 28 | * identifies *NOT NULL columns* and provides values by requesting the database 29 | * adds rows of dependent tables in case of *foreign key constraints* 30 | * sorts insert statements to accommodate *foreign key constraints* 31 | * sorts insert statements following *primary key values* 32 | 33 | _[Another project](https://github.com/quick-perf/quick-sql-test-data-web) provides a web page to ease the use of the _Quick SQL test data_ library._ 34 | 35 | ## How to use the library 36 | 37 | With Maven, you have to add the following dependency: 38 | 39 | ```xml 40 | 41 | 42 | org.quickperf 43 | quick-sql-test-data 44 | 0.1 45 | 46 | ``` 47 | 48 | You can generate the insert statements with the help of an instance of `org.qstd.QuickSqlTestData` class. 49 | 50 | _Quick SQL test data_ works with: 51 | * PostgreSQL 52 | * Oracle 53 | * MariaDB 54 | * MySQL 55 | * Microsoft SQL Server 56 | * H2 57 | * HSQLDB 58 | 59 | ## Use cases 60 | 61 | This library can be helpful in the two following situations. 62 | 63 | ### Create a dataset before starting the writing of an SQL query 64 | 65 | This case happens when you develop SQL queries with *Test-Driven Development* (TDD). 66 | 67 | You can read below an example where we define a dataset row for which we generate the INSERT statement: 68 | ```java 69 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(dataSource); 70 | DatasetRow datasetRow = DatasetRow.ofTable("Player") 71 | .addColumnValue("lastName","Pogba"); 72 | List insertStatements = quickSqlTestData.generateInsertListFor(datasetRow); 73 | 74 | System.out.println(insertStatements); 75 | ``` 76 | 77 | The console displays the following result: 78 | ``` 79 | [INSERT INTO PLAYER(FIRSTNAME, LASTNAME) VALUES('Paul', 'Pogba')] 80 | ``` 81 | FIRSTNAME column owns a NOT NULL constraint. For this reason, the library has retrieved a FIRSTNAME value for the Pogba LASTNAME and has used it in the generated statement. 82 | 83 | ### Test an existing SQL query 84 | Let's take an example: 85 | 86 | ```java 87 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(dataSource); 88 | String selectStatement = "SELECT * FROM Player WHERE LASTNAME = 'Pogba'"; 89 | String insertScript = quickSqlTestData.generateInsertScriptFor(selectStatement); 90 | System.out.println(insertScript); 91 | ``` 92 | 93 | The console displays the following queries: 94 | ``` 95 | INSERT INTO TEAM(ID, NAME) VALUES(1, 'Manchester United'); 96 | INSERT INTO PLAYER(ID, FIRSTNAME, LASTNAME, TEAM_ID) VALUES(1, 'Paul', 'Pogba', 1); 97 | ``` 98 | The library has done its best to generate INSERT queries allowing to test the SELECT query. 99 | It has detected a foreign key constraint and has generated a first statement inserting on a Team table. This one contains a value for the NAME column that must not be null. 100 | 101 | ## License 102 | 103 | [Apache License 2.0](/LICENSE.txt) 104 | -------------------------------------------------------------------------------- /THIRD_PARTY.txt: -------------------------------------------------------------------------------- 1 | Quick SQL test data library uses: 2 | * JSqlParser licensed under Apache Software License, Version 2.0 (https://github.com/JSQLParser/JSqlParser/blob/master/LICENSE_APACHEV2) or LGPL V2.1 (https://github.com/JSQLParser/JSqlParser/blob/master/LICENSE_LGPLV21) 3 | 4 | Quick SQL test data library uses only for testing and doesn't ship with: 5 | * JUnit 5 licensed under Eclipse Public License - v 2.0 (https://github.com/junit-team/junit5/blob/main/LICENSE.md) 6 | * AssertJ licensed under Apache Software License, Version 2.0 (https://github.com/assertj/assertj-core/blob/main/LICENSE.txt) 7 | * AssertJ-DB licensed under Apache Software License, Version 2.0 (https://github.com/assertj/assertj-db/blob/main/LICENSE.txt) 8 | * QuickPerf licensed under Apache Software License, Version 2.0 (https://github.com/quick-perf/quickperf/blob/master/LICENSE.txt) 9 | * Testcontainers licensed under MIT License (https://github.com/testcontainers/testcontainers-java/blob/master/LICENSE) 10 | * HikariCP licensed under Apache Software License, Version 2.0 (https://github.com/brettwooldridge/HikariCP/blob/dev/LICENSE) 11 | * Logback licensed under Eclipse Public License v1.0 or GNU Lesser General Public License version 2.1 (https://github.com/qos-ch/logback/blob/master/LICENSE.txt) 12 | * H2 licensed under Mozilla Public License Version 2.0 or Eclipse Public License v1.0 (https://github.com/h2database/h2database/blob/master/LICENSE.txt) 13 | * MariaDB Docker licenced under GNU General Public License v2.0 (https://github.com/MariaDB/mariadb-docker/blob/master/LICENSE) 14 | * MariaDB java connector licenced under GNU Lesser General Public License version 2.1 (https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/LICENSE) 15 | Please read this: https://mariadb.com/kb/en/licensing-faq/#licenses-used-by-mariadb 16 | * Microsoft SQL Server Docker image licensed under End-User Licensing Agreement (https://hub.docker.com/_/microsoft-mssql-server#environment-variables) 17 | * Microsoft JDBC Driver for SQL Server licensed under MIT License (https://github.com/microsoft/mssql-jdbc/blob/dev/LICENSE) 18 | * PostgreSQL Docker image licensed under MIT License (https://github.com/docker-library/postgres/blob/master/LICENSE) 19 | * PostgreSQL JDBC Driver licensed under BSD 2-Clause "Simplified" License (https://github.com/pgjdbc/pgjdbc/blob/master/LICENSE) 20 | * Oracle Database Express Edition licensed under Oracle Free Use Terms and Conditions (https://docs.oracle.com/en/database/oracle/oracle-database/18/xelic/licensing-information.html#GUID-5D29C372-9B49-4A4D-A9AF-70ACA12CCF46, https://www.oracle.com/downloads/licenses/oracle-free-license.html) 21 | * Oracle JDBC driver licensed under Oracle Free Use Terms and Conditions (https://www.oracle.com/downloads/licenses/oracle-free-license.html) 22 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | 4 | coverage: 5 | round: nearest 6 | precision: 2 7 | 8 | parsers: 9 | gcov: 10 | branch_detection: 11 | conditional: yes 12 | loop: yes 13 | method: no 14 | macro: no -------------------------------------------------------------------------------- /dco.txt: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 1 Letterman Drive 6 | Suite D4700 7 | San Francisco, CA, 94129 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this 10 | license document, but changing it is not allowed. 11 | 12 | 13 | Developer's Certificate of Origin 1.1 14 | 15 | By making a contribution to this project, I certify that: 16 | 17 | (a) The contribution was created in whole or in part by me and I 18 | have the right to submit it under the open source license 19 | indicated in the file; or 20 | 21 | (b) The contribution is based upon previous work that, to the best 22 | of my knowledge, is covered under an appropriate open source 23 | license and I have the right under that license to submit that 24 | work with modifications, whether created in whole or in part 25 | by me, under the same open source license (unless I am 26 | permitted to submit under a different license), as indicated 27 | in the file; or 28 | 29 | (c) The contribution was provided directly to me by some other 30 | person who certified (a), (b) or (c) and I have not modified 31 | it. 32 | 33 | (d) I understand and agree that this project and the contribution 34 | are public and that a record of the contribution (including all 35 | personal information I submit with it, including my sign-off) is 36 | maintained indefinitely and may be redistributed consistent with 37 | this project or the open source license(s) involved. 38 | -------------------------------------------------------------------------------- /license-header.txt: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 2 | the License. You may obtain a copy of the License at 3 | 4 | http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 7 | an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 8 | specific language governing permissions and limitations under the License. 9 | 10 | Copyright ${project.inceptionYear}-${currentYear} the original author or authors. 11 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/ColumnMappingPart.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | public class ColumnMappingPart { 16 | 17 | final String tableName; 18 | final String tableColumn; 19 | private final String tableSchema; 20 | 21 | public ColumnMappingPart(String tableSchema, String tableName, String tableColumn) { 22 | this.tableSchema = tableSchema; 23 | this.tableName = tableName; 24 | this.tableColumn = tableColumn; 25 | } 26 | 27 | boolean hasColumn(String columnName) { 28 | return tableColumn.equals(columnName); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/ColumnNamesComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import java.util.Comparator; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | class ColumnNamesComparator implements Comparator { 21 | 22 | private final Map positionByColumnName; 23 | 24 | private ColumnNamesComparator(Map positionByColumnName) { 25 | this.positionByColumnName = positionByColumnName; 26 | } 27 | 28 | public static ColumnNamesComparator from(List orderedColumns) { 29 | Map positionByColumnName = buildIndexByColumnName(orderedColumns); 30 | return new ColumnNamesComparator(positionByColumnName); 31 | } 32 | 33 | private static Map buildIndexByColumnName(List orderedColumns) { 34 | final Map positionByColumnName = new HashMap<>(); 35 | for (int i = 0; i < orderedColumns.size(); i++) { 36 | positionByColumnName.put(orderedColumns.get(i), i + 1); 37 | } 38 | return positionByColumnName; 39 | } 40 | 41 | @Override 42 | public int compare(String colName1, String colName2) { 43 | return findPositionOf(colName1) - findPositionOf(colName2); 44 | } 45 | 46 | private int findPositionOf(String colName1) { 47 | return positionByColumnName.get(colName1); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/ColumnNamesExtractor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import net.sf.jsqlparser.expression.BinaryExpression; 16 | import net.sf.jsqlparser.expression.Expression; 17 | import net.sf.jsqlparser.expression.ExpressionVisitorAdapter; 18 | import net.sf.jsqlparser.schema.Column; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Collection; 22 | import java.util.HashSet; 23 | import java.util.Set; 24 | 25 | class ColumnNamesExtractor { 26 | 27 | static final ColumnNamesExtractor INSTANCE = new ColumnNamesExtractor(); 28 | 29 | private ColumnNamesExtractor() { 30 | } 31 | 32 | private static class ColumnExpressionVisitor extends ExpressionVisitorAdapter { 33 | 34 | private String visitedColumnName; 35 | 36 | @Override 37 | public void visit(Column column) { 38 | this.visitedColumnName = column.getColumnName(); 39 | } 40 | 41 | String getVisitedColumnName() { 42 | return visitedColumnName; 43 | } 44 | 45 | } 46 | 47 | Set findColumnNamesOf(Expression expression) { 48 | Set columnNames = new HashSet<>(); 49 | if (expression instanceof BinaryExpression) { 50 | // AndExpression, OrExpression, LikeExpression, ... 51 | BinaryExpression binaryExpression = (BinaryExpression) expression; 52 | Collection leftRightColumnNames = extractColumnNamesOf(binaryExpression); 53 | columnNames.addAll(leftRightColumnNames); 54 | } else if (expression != null) { 55 | // Column names 56 | ColumnExpressionVisitor columnExpressionVisitor = new ColumnExpressionVisitor(); 57 | expression.accept(columnExpressionVisitor); 58 | String visitedColumnName = columnExpressionVisitor.getVisitedColumnName(); 59 | if(visitedColumnName != null) { 60 | columnNames.add(visitedColumnName); 61 | } 62 | } 63 | return columnNames; 64 | } 65 | 66 | private Collection extractColumnNamesOf(BinaryExpression binaryExpression) { 67 | Collection leftRightColumnNames = new ArrayList<>(); 68 | Collection leftColumnNames = findColumnNamesOf(binaryExpression.getLeftExpression()); 69 | Collection rightColumnNames = findColumnNamesOf(binaryExpression.getRightExpression()); 70 | leftRightColumnNames.addAll(leftColumnNames); 71 | leftRightColumnNames.addAll(rightColumnNames); 72 | return leftRightColumnNames; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/ColumnOrdersFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import java.util.List; 16 | 17 | public interface ColumnOrdersFinder { 18 | 19 | List findDatabaseColumnOrdersOf(String tableName); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/ColumnValueFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import java.time.OffsetDateTime; 16 | import java.time.format.DateTimeFormatter; 17 | import java.time.format.DateTimeFormatterBuilder; 18 | import org.qstd.dbtype.DatabaseType; 19 | 20 | import java.sql.Time; 21 | import java.sql.Timestamp; 22 | import java.time.OffsetTime; 23 | import java.util.Calendar; 24 | 25 | class ColumnValueFormatter { 26 | 27 | static final ColumnValueFormatter INSTANCE = new ColumnValueFormatter(); 28 | 29 | private ColumnValueFormatter() { } 30 | 31 | String formatColumnValue(Object columnValue, DatabaseType dbType) { 32 | if(columnValue == null) { 33 | return "NULL"; 34 | } else if(DatabaseType.ORACLE.equals(dbType) 35 | && columnValue instanceof Timestamp) { 36 | Timestamp timeStamp = (Timestamp) columnValue; 37 | return buildOracleToDateFunctionFor(timeStamp); 38 | } else if(DatabaseType.ORACLE.equals(dbType) 39 | && isOracleSqlTimestamp(columnValue)) { 40 | return buildOracleToTimeStampFunctionFor(columnValue); 41 | }else if(DatabaseType.HSQLDB.equals(dbType) 42 | && columnValue instanceof OffsetDateTime){ 43 | OffsetDateTime offsetDateTime = (OffsetDateTime) columnValue; 44 | return formatForHsqlDBOffsetDateTime(offsetDateTime); 45 | } else if (columnValue instanceof String 46 | || columnValue instanceof java.sql.Date 47 | || columnValue instanceof Timestamp 48 | || columnValue instanceof Time 49 | || columnValue instanceof OffsetTime 50 | || isTimestampWithTimeZoneH2Type(columnValue) 51 | || isMicrosoftDateTimeOffset(columnValue)) { 52 | String stringColumnValue = columnValue.toString(); 53 | return "'" + stringColumnValue + "'"; 54 | } 55 | return columnValue.toString(); 56 | } 57 | 58 | private boolean isMicrosoftDateTimeOffset(Object columnValue) { 59 | Class columnValueClass = columnValue.getClass(); 60 | String classCanonicalName = columnValueClass.getCanonicalName(); 61 | return "microsoft.sql.DateTimeOffset".equals(classCanonicalName); 62 | } 63 | 64 | private String formatForHsqlDBOffsetDateTime(OffsetDateTime offsetDateTime) { 65 | DateTimeFormatter fmt = new DateTimeFormatterBuilder() 66 | .appendPattern("yyyy-MM-dd HH:mm:ss") 67 | .parseLenient() 68 | .appendOffset("+HH:MM", "Z") 69 | .toFormatter(); 70 | return "'" + fmt.format(offsetDateTime) + "'"; 71 | } 72 | 73 | private String buildOracleToDateFunctionFor(Timestamp timeStamp) { 74 | //https://stackoverflow.com/questions/9180014/using-oracle-to-date-function-for-date-string-with-milliseconds 75 | // "An Oracle DATE does not store times with more precision than a second." 76 | Calendar calendar = Calendar.getInstance(); 77 | calendar.setTime(timeStamp); 78 | int monthNumber = calendar.get(Calendar.MONTH) + 1; 79 | int secondNumber = calendar.get(Calendar.SECOND); 80 | String toDateString = calendar.get(Calendar.YEAR) 81 | + "-" + (monthNumber < 10 ? "0" : "") + monthNumber 82 | + "-" + calendar.get(Calendar.DAY_OF_MONTH) 83 | + "-" + calendar.get(Calendar.HOUR_OF_DAY) 84 | + "-" + calendar.get(Calendar.MINUTE) 85 | + "-" + (secondNumber < 10 ? "0" : "") + secondNumber; 86 | return "TO_DATE('" + toDateString + "', 'yyyy-mm-dd-HH24-mi-ss')"; 87 | } 88 | 89 | private boolean isOracleSqlTimestamp(Object columnValue) { 90 | Class columnValueClass = columnValue.getClass(); 91 | String classCanonicalName = columnValueClass.getCanonicalName(); 92 | return classCanonicalName.equals("oracle.sql.TIMESTAMP"); 93 | } 94 | 95 | private String buildOracleToTimeStampFunctionFor(Object columnValue) { 96 | String oracleTimeStampAsString = columnValue.toString(); 97 | String aDateWithMsLessThan100 = "2012-09-17 19:56:47.10"; 98 | boolean dateHasMsLessThan100 = oracleTimeStampAsString.length() == aDateWithMsLessThan100.length(); 99 | String dateForTimeStampCreation = dateHasMsLessThan100 ? oracleTimeStampAsString + "0" : oracleTimeStampAsString; 100 | return "TO_TIMESTAMP('" + dateForTimeStampCreation 101 | + "', 'YYYY-MM-DD HH24:MI:SS.FF')"; 102 | } 103 | 104 | private boolean isTimestampWithTimeZoneH2Type(Object columnValue) { 105 | Class columnValueClass = columnValue.getClass(); 106 | String classCanonicalName = columnValueClass.getCanonicalName(); 107 | return classCanonicalName.equals("org.h2.api.TimestampWithTimeZone"); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/ColumnsMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | public class ColumnsMapping { 16 | 17 | private final ColumnMappingPart columnMappingPart1; 18 | 19 | private final ColumnMappingPart columnMappingPart2; 20 | 21 | public ColumnsMapping(ColumnMappingPart columnMappingPart1, ColumnMappingPart columnMappingPart2) { 22 | this.columnMappingPart1 = columnMappingPart1; 23 | this.columnMappingPart2 = columnMappingPart2; 24 | } 25 | 26 | boolean hasMappingForColumn(String columnName) { 27 | return columnMappingPart1.hasColumn(columnName); 28 | } 29 | 30 | ColumnMappingPart getMapping() { 31 | return columnMappingPart2; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/ColumnsMappingGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Collection; 17 | import java.util.Collections; 18 | import java.util.Optional; 19 | 20 | public class ColumnsMappingGroup { 21 | 22 | public static final ColumnsMappingGroup NO_MAPPING = new ColumnsMappingGroup(Collections.emptyList()); 23 | 24 | private final Collection columnsMappings; 25 | 26 | public ColumnsMappingGroup(Collection columnsMappings) { 27 | this.columnsMappings = new ArrayList<>(columnsMappings); 28 | } 29 | 30 | Optional findMappingForColumn(String columnName) { 31 | return columnsMappings 32 | .stream() 33 | .filter(columnsMapping -> columnsMapping.hasMappingForColumn(columnName)) 34 | .map(ColumnsMapping::getMapping) 35 | .findFirst(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/ColumnsMappingsFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | public interface ColumnsMappingsFinder { 16 | 17 | ColumnsMappingGroup findColumnsMappingsOf(String tableName); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/DatabaseMetadataFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import org.qstd.dbtype.DatabaseMetadataFinderFactory; 16 | import org.qstd.dbtype.DatabaseMetadataFinderWithCache; 17 | 18 | import java.util.function.Function; 19 | 20 | /** 21 | * Interface describing the methods needed by the library to retrieve some database metadata. 22 | * 23 | * @see DatabaseMetadataFinderFactory 24 | * @see DatabaseMetadataFinderWithCache 25 | */ 26 | public interface DatabaseMetadataFinder extends NotNullColumnsFinder 27 | , ColumnOrdersFinder 28 | , ReferencedTablesFinder 29 | , ColumnsMappingsFinder 30 | , PrimaryKeyColumnsFinder { 31 | 32 | default Function getFunctionToHaveMetadataTableName() { 33 | return tableName -> tableName; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/DatasetRow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import java.util.*; 16 | import java.util.function.Function; 17 | import java.util.stream.Stream; 18 | 19 | import static java.util.stream.Collectors.toList; 20 | 21 | public class DatasetRow { 22 | 23 | private String tableName; 24 | 25 | private TreeMap columnValueByColumnName = new TreeMap<>(); 26 | 27 | private DatasetRow(String tableName) { 28 | this.tableName = tableName; 29 | } 30 | 31 | public static DatasetRow ofTable(String tableName) { 32 | return new DatasetRow(tableName); 33 | } 34 | 35 | protected void addColumnValues(Map columnValues) { 36 | columnValueByColumnName.putAll(columnValues); 37 | } 38 | 39 | Set getColumnNames() { 40 | return columnValueByColumnName.keySet(); 41 | } 42 | 43 | Collection getColumnValues() { 44 | return columnValueByColumnName.values(); 45 | } 46 | 47 | Map getColumnValueByColumnName() { 48 | return new HashMap<>(columnValueByColumnName); 49 | } 50 | 51 | boolean hasNotNullValueForColumn(String columnName) { 52 | return columnValueByColumnName.get(columnName) != null; 53 | } 54 | 55 | void sortColumnsFollowing(List databaseColumnOrders) { 56 | if (!databaseColumnOrders.isEmpty()) { 57 | ColumnNamesComparator columnNamesComparator = ColumnNamesComparator.from(databaseColumnOrders); 58 | TreeMap columnValueByColumnName = new TreeMap<>(columnNamesComparator); 59 | columnValueByColumnName.putAll(this.columnValueByColumnName); 60 | this.columnValueByColumnName = columnValueByColumnName; 61 | } 62 | } 63 | 64 | boolean mergeWithARowOf(Collection datasetRows) { 65 | Optional optionalRowToMergeWith = searchARowToMergeIn(datasetRows); 66 | if(optionalRowToMergeWith.isPresent()) { 67 | DatasetRow rowToMergeWith = optionalRowToMergeWith.get(); 68 | rowToMergeWith.addValuesOf(this); 69 | return true; 70 | } 71 | return false; 72 | } 73 | 74 | private Optional searchARowToMergeIn(Collection datasetRows) { 75 | return datasetRows 76 | .stream() 77 | .filter(this::isMergeableWith) 78 | .findFirst(); 79 | } 80 | 81 | void addValuesOf(DatasetRow datasetRow) { 82 | TreeMap columnValueByColumnName = datasetRow.columnValueByColumnName; 83 | for (Map.Entry columnValueOfColumnName : columnValueByColumnName.entrySet()) { 84 | Object value = columnValueOfColumnName.getValue(); 85 | if(value != null) { 86 | String columnName = columnValueOfColumnName.getKey(); 87 | this.columnValueByColumnName.put(columnName, value); 88 | } 89 | } 90 | } 91 | 92 | boolean isMergeableWith(DatasetRow otherDatasetRow) { 93 | if(!this.tableName.equals(otherDatasetRow.getTableName())) { 94 | return false; 95 | } 96 | return sameNotNullColumns(otherDatasetRow); 97 | } 98 | 99 | String getTableName() { 100 | return tableName; 101 | } 102 | 103 | private boolean sameNotNullColumns(DatasetRow otherDatasetRow) { 104 | TreeMap columnValueByColumnName = otherDatasetRow.columnValueByColumnName; 105 | for (Map.Entry columnValueOfColumnName : columnValueByColumnName.entrySet()) { 106 | Object mergeableValue = columnValueOfColumnName.getValue(); 107 | if(mergeableValue != null) { 108 | String column = columnValueOfColumnName.getKey(); 109 | Object value = this.columnValueByColumnName.get(column); 110 | if(!mergeableValue.equals(value) && value != null) { 111 | return false; 112 | } 113 | } 114 | } 115 | 116 | return true; 117 | } 118 | 119 | Collection extractJoinedRowsFrom(ColumnsMappingGroup columnsMappingGroup) { 120 | return columnValueByColumnName 121 | .entrySet() 122 | .stream() 123 | .map(valueForColumn -> { 124 | String column = valueForColumn.getKey(); 125 | Optional optionalMappingForColumn = 126 | columnsMappingGroup.findMappingForColumn(column); 127 | return buildOptionalRowFrom(valueForColumn, optionalMappingForColumn); 128 | }) 129 | .flatMap(DatasetRow::streamOf) 130 | .collect(toList()); 131 | } 132 | 133 | private Optional buildOptionalRowFrom(Map.Entry valueForColumn, Optional optionalMappingForColumn) { 134 | return optionalMappingForColumn.map(columnMappingPart -> { 135 | Object value = valueForColumn.getValue(); 136 | DatasetRow joinedRow = new DatasetRow(columnMappingPart.tableName); 137 | joinedRow.addColumnValue(columnMappingPart.tableColumn, value); 138 | return joinedRow; 139 | }); 140 | } 141 | 142 | private static Stream streamOf(Optional optional) { 143 | return optional.map(Stream::of).orElseGet(Stream::empty); 144 | } 145 | 146 | public DatasetRow addColumnValue(String columnName, Object value) { 147 | columnValueByColumnName.put(columnName, value); 148 | return this; 149 | } 150 | 151 | Object getValueOf(String columnName) { 152 | return columnValueByColumnName.get(columnName); 153 | } 154 | 155 | void updateTableNameWith(Function tableNameFunction) { 156 | String newTableName = tableNameFunction.apply(tableName); 157 | tableName = newTableName; 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/DatasetRowComparatorBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import java.math.BigDecimal; 16 | import java.util.Comparator; 17 | import java.util.List; 18 | 19 | class DatasetRowComparatorBuilder { 20 | 21 | private DatasetRowComparatorBuilder() { } 22 | 23 | static Comparator buildFrom(DatabaseMetadataFinder databaseMetadataFinder) { 24 | ComparatorOnTableDependencies comparatorOnTableDependencies = new ComparatorOnTableDependencies(databaseMetadataFinder); 25 | ComparatorOnPrimaryKey comparatorOnPrimaryKey = new ComparatorOnPrimaryKey(databaseMetadataFinder); 26 | ComparatorOnTableName comparatorOnTableName = new ComparatorOnTableName(); 27 | return comparatorOnTableDependencies.thenComparing(comparatorOnPrimaryKey) 28 | .thenComparing(comparatorOnTableName); 29 | } 30 | 31 | private static class ComparatorOnPrimaryKey implements Comparator { 32 | 33 | private final PrimaryKeyColumnsFinder primaryKeyColumnsFinder; 34 | 35 | ComparatorOnPrimaryKey(PrimaryKeyColumnsFinder primaryKeyColumnsFinder) { 36 | this.primaryKeyColumnsFinder = primaryKeyColumnsFinder; 37 | } 38 | 39 | @Override 40 | public int compare(DatasetRow datasetRow1, DatasetRow datasetRow2) { 41 | 42 | if(!sameTableNames(datasetRow1, datasetRow2)) { 43 | return 0; 44 | } 45 | 46 | String tableName = datasetRow1.getTableName(); 47 | List primaryKeyColumns = primaryKeyColumnsFinder.findPrimaryColumnsOf(tableName); 48 | 49 | for (String primaryKeyColumn : primaryKeyColumns) { 50 | int intComparison = compareIntPkValues(primaryKeyColumn, datasetRow1, datasetRow2); 51 | if(intComparison != 0) { 52 | return intComparison; 53 | } 54 | } 55 | 56 | return 0; 57 | 58 | } 59 | 60 | private boolean sameTableNames(DatasetRow datasetRow1, DatasetRow datasetRow2) { 61 | return datasetRow1.getTableName().equals(datasetRow2.getTableName()); 62 | } 63 | 64 | private int compareIntPkValues(String primaryKeyColumn, DatasetRow datasetRow1, DatasetRow datasetRow2) { 65 | Object pkValue1 = datasetRow1.getValueOf(primaryKeyColumn); 66 | Object pkValue2 = datasetRow2.getValueOf(primaryKeyColumn); 67 | boolean integerPrimaryKey = pkValue1 instanceof Integer 68 | || pkValue1 instanceof Long 69 | || pkValue1 instanceof BigDecimal; 70 | if(integerPrimaryKey) { 71 | Number pkValue1AsNumber = (Number) pkValue1; 72 | Number pkValue2AsNumber = (Number) pkValue2; 73 | long pkValue1AsLong = pkValue1AsNumber.longValue(); 74 | long pkValue2AsLong = pkValue2AsNumber.longValue(); 75 | return Long.compare(pkValue1AsLong, pkValue2AsLong ); 76 | } 77 | return 0; 78 | } 79 | 80 | } 81 | 82 | private static class ComparatorOnTableDependencies implements Comparator { 83 | 84 | private final ReferencedTablesFinder referencedTablesFinder; 85 | 86 | public ComparatorOnTableDependencies(ReferencedTablesFinder referencedTablesFinder) { 87 | this.referencedTablesFinder = referencedTablesFinder; 88 | } 89 | 90 | @Override 91 | public int compare(DatasetRow datasetRow1, DatasetRow datasetRow2) { 92 | 93 | String tableName1 = datasetRow1.getTableName(); 94 | String tableName2 = datasetRow2.getTableName(); 95 | 96 | ReferencedTableSet referencedTableSetOfTable1 97 | = referencedTablesFinder.findReferencedTablesOf(tableName1); 98 | if(referencedTableSetOfTable1.referencesTable(tableName2)) { 99 | return 1; 100 | } 101 | 102 | ReferencedTableSet referencedTableSetOfTable2 103 | = referencedTablesFinder.findReferencedTablesOf(tableName2); 104 | if(referencedTableSetOfTable2.referencesTable(tableName1)) { 105 | return -1; 106 | } 107 | 108 | return 0; 109 | 110 | } 111 | 112 | } 113 | 114 | private static class ComparatorOnTableName implements Comparator { 115 | 116 | @Override 117 | public int compare(DatasetRow row1, DatasetRow row2) { 118 | String row1TableName = row1.getTableName(); 119 | String row2TableName = row2.getTableName(); 120 | return row1TableName.compareTo(row2TableName); 121 | } 122 | 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/DatasetRowSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import org.qstd.dbtype.DatabaseType; 16 | 17 | import javax.sql.DataSource; 18 | import java.util.*; 19 | import java.util.function.Function; 20 | 21 | class DatasetRowSet { 22 | 23 | private final MissingNotNullColumnsFinder missingNotNullColumnsFinder; 24 | 25 | private final DatabaseMetadataFinder databaseMetadataFinder; 26 | 27 | private final Collection datasetRows = new ArrayDeque<>(); 28 | 29 | DatasetRowSet( DataSource dataSource 30 | , DatabaseType dbType 31 | , DatabaseMetadataFinder databaseMetadataFinder) { 32 | this.databaseMetadataFinder = databaseMetadataFinder; 33 | this.missingNotNullColumnsFinder = new MissingNotNullColumnsFinder(dataSource 34 | , dbType 35 | , databaseMetadataFinder); 36 | } 37 | 38 | void add(Collection datasetRows) { 39 | for (DatasetRow datasetRow : datasetRows) { 40 | add(datasetRow); 41 | } 42 | } 43 | 44 | private void add(DatasetRow datasetRow) { 45 | 46 | Function functionToHaveMetadataTableName = databaseMetadataFinder.getFunctionToHaveMetadataTableName(); 47 | datasetRow.updateTableNameWith(functionToHaveMetadataTableName); 48 | 49 | boolean rowIsMerged = datasetRow.mergeWithARowOf(datasetRows); 50 | 51 | if (!rowIsMerged) { 52 | Map missingNotNullColumns = 53 | missingNotNullColumnsFinder.findMissingNoNullColumnsOf(datasetRow); 54 | datasetRow.addColumnValues(missingNotNullColumns); 55 | 56 | datasetRows.add(datasetRow); 57 | 58 | Collection joinedRows = findJoinedRowsOf(datasetRow); 59 | for (DatasetRow joinRow : joinedRows) { 60 | add(joinRow); 61 | } 62 | } 63 | 64 | } 65 | 66 | private Collection findJoinedRowsOf(DatasetRow datasetRow) { 67 | String tableName = datasetRow.getTableName(); 68 | ColumnsMappingGroup columnsMappingGroup = 69 | databaseMetadataFinder.findColumnsMappingsOf(tableName); 70 | return datasetRow.extractJoinedRowsFrom(columnsMappingGroup); 71 | } 72 | 73 | List sort() { 74 | sortColumnsFollowingDatabaseDeclaration(datasetRows); 75 | return sortRows(); 76 | } 77 | 78 | private void sortColumnsFollowingDatabaseDeclaration(Collection allRows) { 79 | for (DatasetRow datasetRow : allRows) { 80 | String tableName = datasetRow.getTableName(); 81 | List databaseColumnOrders = databaseMetadataFinder.findDatabaseColumnOrdersOf(tableName); 82 | datasetRow.sortColumnsFollowing(databaseColumnOrders); 83 | } 84 | } 85 | 86 | private List sortRows() { 87 | List rowsAsList = new ArrayList<>(datasetRows); 88 | Comparator datasetRowComparator = 89 | DatasetRowComparatorBuilder.buildFrom(databaseMetadataFinder); 90 | rowsAsList.sort(datasetRowComparator); 91 | return rowsAsList; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/DatasetRowsFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import net.sf.jsqlparser.JSQLParserException; 16 | import net.sf.jsqlparser.parser.CCJSqlParserUtil; 17 | import net.sf.jsqlparser.statement.Statement; 18 | import net.sf.jsqlparser.statement.select.Select; 19 | import net.sf.jsqlparser.util.TablesNamesFinder; 20 | 21 | import javax.sql.DataSource; 22 | import java.sql.*; 23 | import java.util.*; 24 | 25 | import static java.util.Collections.emptyList; 26 | import static org.qstd.SelectTransformerFactory.createSelectTransformer; 27 | 28 | class DatasetRowsFinder { 29 | 30 | private final DataSource dataSource; 31 | 32 | DatasetRowsFinder(DataSource dataSource) { 33 | this.dataSource = dataSource; 34 | } 35 | 36 | Collection findDatasetRowsOf(SqlQuery sqlQuery) { 37 | 38 | SelectTransformer selectTransformer = createSelectTransformer(sqlQuery); 39 | Optional optionalSelectQuery = selectTransformer.toSelect(sqlQuery); 40 | 41 | if (optionalSelectQuery.isPresent()) { 42 | SqlQuery selectQuery = optionalSelectQuery.get(); 43 | return execute(selectQuery); 44 | } 45 | 46 | return emptyList(); 47 | } 48 | 49 | private Collection execute(SqlQuery sqlQuery) { 50 | 51 | List datasetRowsToReturn = new ArrayList<>(); 52 | 53 | try (Connection connection = dataSource.getConnection(); 54 | PreparedStatement selectStatement = PreparedStatementBuilder.buildFrom(sqlQuery, connection)) { 55 | 56 | ResultSet resultSet = selectStatement.executeQuery(); 57 | 58 | ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); 59 | int columnCount = resultSetMetaData.getColumnCount(); 60 | 61 | while (resultSet.next()) { 62 | Collection datasetRows = 63 | buildDatasetRowsFrom(resultSet, resultSetMetaData 64 | , columnCount, sqlQuery); 65 | datasetRowsToReturn.addAll(datasetRows); 66 | } 67 | } catch (SQLException sqlException) { 68 | sqlException.printStackTrace(); 69 | } 70 | 71 | return datasetRowsToReturn; 72 | 73 | } 74 | 75 | private Collection buildDatasetRowsFrom(ResultSet resultSet, ResultSetMetaData resultSetMetaData 76 | , int columnCount, SqlQuery sqlQuery) throws SQLException { 77 | Map rowsByTableName = new HashMap<>(); 78 | for (int colIndex = 1; colIndex <= columnCount; colIndex++) { 79 | final String tableName = findTableName(resultSetMetaData, colIndex, sqlQuery); 80 | DatasetRow datasetRow = 81 | rowsByTableName.computeIfAbsent(tableName 82 | , t -> DatasetRow.ofTable(tableName)); 83 | 84 | String column = resultSetMetaData.getColumnName(colIndex); 85 | Object value = resultSet.getObject(colIndex); 86 | datasetRow.addColumnValue(column, value); 87 | } 88 | return rowsByTableName.values(); 89 | } 90 | 91 | private String findTableName(ResultSetMetaData resultSetMetaData, int colIndex, SqlQuery sqlQuery) throws SQLException { 92 | String tableName = resultSetMetaData.getTableName(colIndex); 93 | if (!tableName.isEmpty()) { 94 | return tableName; 95 | } 96 | String queryAsString = sqlQuery.getQueryAsString(); 97 | return extractTableNameFrom(queryAsString); 98 | } 99 | 100 | private String extractTableNameFrom(String sqlQueryAsString) { 101 | try { 102 | Statement statement = CCJSqlParserUtil.parse(sqlQueryAsString); 103 | Select select = (Select) statement; 104 | TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); 105 | List tableList = tablesNamesFinder.getTableList(select); 106 | if(tableList.size() == 1) { 107 | return tableList.get(0); 108 | } 109 | } catch (JSQLParserException e) { 110 | e.printStackTrace(); 111 | } 112 | return ""; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/DatasetRowsGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import org.qstd.dbtype.DatabaseType; 16 | 17 | import javax.sql.DataSource; 18 | import java.util.Collection; 19 | import java.util.List; 20 | 21 | class DatasetRowsGenerator { 22 | 23 | private final DataSource dataSource; 24 | 25 | private final DatabaseType dbType; 26 | 27 | private final DatabaseMetadataFinder databaseMetadataFinder; 28 | 29 | private final DatasetRowsFinder datasetRowsFinder; 30 | 31 | DatasetRowsGenerator(DataSource dataSource 32 | , DatabaseType dbType 33 | , DatabaseMetadataFinder databaseMetadataFinder) { 34 | this.dataSource = dataSource; 35 | this.dbType = dbType; 36 | this.databaseMetadataFinder = databaseMetadataFinder; 37 | this.datasetRowsFinder = new DatasetRowsFinder(dataSource); 38 | } 39 | 40 | List generateDatasetRowsFor(List sqlQueries) { 41 | DatasetRowSet datasetRowSet = new DatasetRowSet(dataSource, dbType, databaseMetadataFinder); 42 | for (SqlQuery sqlQuery : sqlQueries) { 43 | Collection datasetRows = datasetRowsFinder.findDatasetRowsOf(sqlQuery); 44 | datasetRowSet.add(datasetRows); 45 | } 46 | return datasetRowSet.sort(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/DeleteToSelectTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import net.sf.jsqlparser.expression.Expression; 16 | import net.sf.jsqlparser.statement.delete.Delete; 17 | 18 | import java.util.Optional; 19 | 20 | class DeleteToSelectTransformer implements SelectTransformer { 21 | 22 | private Delete deleteStatement; 23 | 24 | DeleteToSelectTransformer(Delete delete) { 25 | deleteStatement = delete; 26 | } 27 | 28 | @Override 29 | public Optional toSelect(SqlQuery sqlQuery) { 30 | String deleteAsString = sqlQuery.getQueryAsString(); 31 | String deleteString = toSelect(deleteAsString); 32 | SqlQuery deleteQuery = new SqlQuery(deleteString); 33 | return Optional.of(deleteQuery); 34 | } 35 | 36 | private String toSelect(String sqlQueryAsString) { 37 | String tableName = deleteStatement.getTable().getName(); 38 | 39 | String whereClauseAsString = findWhereClauseAsString(); 40 | 41 | return " SELECT " + "*" 42 | + " FROM " + tableName 43 | + whereClauseAsString; 44 | } 45 | 46 | private String findWhereClauseAsString() { 47 | Expression whereExpression = deleteStatement.getWhere(); 48 | String whereClauseAsString = whereExpression == null ? "" 49 | :" WHERE " + whereExpression; 50 | return whereClauseAsString; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/InsertStatementsGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import org.qstd.dbtype.DatabaseType; 16 | 17 | import java.util.Collection; 18 | import java.util.List; 19 | import java.util.Set; 20 | 21 | import static java.lang.System.lineSeparator; 22 | import static java.util.stream.Collectors.joining; 23 | import static java.util.stream.Collectors.toList; 24 | 25 | class InsertStatementsGenerator { 26 | 27 | private final DatabaseType dbType; 28 | 29 | InsertStatementsGenerator(DatabaseType dbType) { 30 | this.dbType = dbType; 31 | } 32 | 33 | String generateInsertScriptFor(List datasetRows) { 34 | return datasetRows 35 | .stream() 36 | .map(this::generateInsertStatementFrom) 37 | .map(insertStatement -> insertStatement + ";" + lineSeparator()) 38 | .collect(joining()); 39 | } 40 | 41 | private String generateInsertStatementFrom(DatasetRow datasetRow) { 42 | String tableName = datasetRow.getTableName(); 43 | Set columnNames = datasetRow.getColumnNames(); 44 | Collection columnValues = datasetRow.getColumnValues(); 45 | return "INSERT INTO " + tableName + "(" + formatColumnNames(columnNames) + ")" 46 | + " VALUES(" + formatColumnValues(columnValues) + ")"; 47 | } 48 | 49 | private String formatColumnNames(Set columnNames) { 50 | return String.join(", ", columnNames); 51 | } 52 | 53 | private String formatColumnValues(Collection columnValues) { 54 | return columnValues 55 | .stream() 56 | .map(columnValue -> ColumnValueFormatter.INSTANCE 57 | .formatColumnValue(columnValue, dbType)) 58 | .collect(joining(", ")); 59 | } 60 | 61 | List generateInsertStatementsFor(List datasetRows) { 62 | return datasetRows 63 | .stream() 64 | .map(this::generateInsertStatementFrom) 65 | .collect(toList()); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/MissingNotNullColumnsFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import org.qstd.dbtype.DatabaseType; 16 | 17 | import javax.sql.DataSource; 18 | import java.util.Collection; 19 | import java.util.Collections; 20 | import java.util.Map; 21 | 22 | import static java.util.stream.Collectors.toList; 23 | 24 | class MissingNotNullColumnsFinder { 25 | 26 | private final DataSource dataSource; 27 | 28 | private final DatabaseType dbType; 29 | 30 | private final DatabaseMetadataFinder databaseMetadataFinder; 31 | 32 | MissingNotNullColumnsFinder(DataSource dataSource, DatabaseType dbType, DatabaseMetadataFinder databaseMetadataFinder) { 33 | this.dataSource = dataSource; 34 | this.dbType = dbType; 35 | this.databaseMetadataFinder = databaseMetadataFinder; 36 | } 37 | 38 | Map findMissingNoNullColumnsOf(DatasetRow datasetRow) { 39 | 40 | String tableName = datasetRow.getTableName(); 41 | 42 | Collection notNullColumns = databaseMetadataFinder.findNotNullColumnsOf(tableName); 43 | 44 | Collection missingNotNullColumns = notNullColumns 45 | .stream() 46 | .filter(columnName -> !datasetRow.hasNotNullValueForColumn(columnName)) 47 | .collect(toList()); 48 | 49 | if (!missingNotNullColumns.isEmpty()) { 50 | RowFinder rowFinder = new RowFinder(dataSource, dbType); 51 | DatasetRow datasetRowWithMissingNotNullColumns = rowFinder.findOneRowFrom(datasetRow.getTableName(), missingNotNullColumns, datasetRow); 52 | return datasetRowWithMissingNotNullColumns.getColumnValueByColumnName(); 53 | } 54 | 55 | return Collections.emptyMap(); 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/NotNullColumnsFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import java.util.Collection; 16 | 17 | public interface NotNullColumnsFinder { 18 | 19 | Collection findNotNullColumnsOf(String tableName); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/PreparedStatementBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import java.sql.Connection; 16 | import java.sql.PreparedStatement; 17 | import java.sql.SQLException; 18 | import java.util.List; 19 | 20 | public class PreparedStatementBuilder { 21 | 22 | private PreparedStatementBuilder() { 23 | } 24 | 25 | public static PreparedStatement buildFrom(SqlQuery sqlQuery, Connection connection) throws SQLException { 26 | String queryAsString = sqlQuery.getQueryAsString(); 27 | PreparedStatement preparedStatement = connection.prepareStatement(queryAsString); 28 | List parameters = sqlQuery.getParameters(); 29 | for (int i = 0; i < parameters.size(); i++) { 30 | preparedStatement.setObject(i + 1, parameters.get(i)); 31 | } 32 | return preparedStatement; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/PrimaryKeyColumnsFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import java.util.List; 16 | 17 | public interface PrimaryKeyColumnsFinder { 18 | 19 | List findPrimaryColumnsOf(String tableName); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/ReferencedTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | public class ReferencedTable { 16 | 17 | private final String tableName; 18 | 19 | private final String referencedTableName; 20 | 21 | private final int level; 22 | 23 | public ReferencedTable(String tableName, String referencedTableName, int level) { 24 | this.tableName = tableName; 25 | this.referencedTableName = referencedTableName; 26 | this.level = level; 27 | } 28 | 29 | boolean references(String tableName) { 30 | return tableName.equals(referencedTableName); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/ReferencedTableSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import java.util.Collection; 16 | 17 | import static java.util.Collections.emptyList; 18 | 19 | public class ReferencedTableSet { 20 | 21 | public static final ReferencedTableSet NONE = new ReferencedTableSet(emptyList()); 22 | 23 | private final Collection referencedTablesOfTable; 24 | 25 | public ReferencedTableSet(Collection referencedTablesOfTable) { 26 | this.referencedTablesOfTable = referencedTablesOfTable; 27 | } 28 | 29 | boolean referencesTable(String tableName) { 30 | for (ReferencedTable referencedTable : referencedTablesOfTable) { 31 | if (referencedTable.references(tableName)) { 32 | return true; 33 | } 34 | } 35 | return false; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/ReferencedTablesFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | public interface ReferencedTablesFinder { 16 | 17 | ReferencedTableSet findReferencedTablesOf(String tableName); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/RowFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import org.qstd.dbtype.DatabaseType; 16 | 17 | import javax.sql.DataSource; 18 | import java.sql.Connection; 19 | import java.sql.PreparedStatement; 20 | import java.sql.ResultSet; 21 | import java.sql.SQLException; 22 | import java.util.Collection; 23 | 24 | class RowFinder { 25 | 26 | private final DataSource dataSource; 27 | 28 | private final DatabaseType dbType; 29 | 30 | RowFinder(DataSource dataSource, DatabaseType dbType) { 31 | this.dataSource = dataSource; 32 | this.dbType = dbType; 33 | } 34 | 35 | DatasetRow findOneRowFrom(String tableName 36 | , Collection columnNamesToSearch 37 | , DatasetRow rowToSearch) { 38 | 39 | SqlQuery missingColumnValuesQuery = 40 | SqlQuery.buildFromRow(columnNamesToSearch, rowToSearch, dbType); 41 | 42 | DatasetRow missingColumnValues = DatasetRow.ofTable(tableName); 43 | try (Connection connection = dataSource.getConnection(); 44 | PreparedStatement missingColumnStatement = PreparedStatementBuilder.buildFrom(missingColumnValuesQuery, connection)) { 45 | ResultSet queryResult = missingColumnStatement.executeQuery(); 46 | 47 | queryResult.next(); // We keep only the first row found 48 | 49 | for (String missingColumnName : columnNamesToSearch) { 50 | Object columnValue = queryResult.getObject(missingColumnName); 51 | missingColumnValues.addColumnValue(missingColumnName, columnValue); 52 | } 53 | } catch (SQLException sqlException) { 54 | System.err.println("Unable to execute " + missingColumnValuesQuery); 55 | sqlException.printStackTrace(); 56 | } 57 | return missingColumnValues; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/SelectTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import java.util.Optional; 16 | 17 | interface SelectTransformer { 18 | 19 | SelectTransformer NO_SELECT_TRANSFORMER = new SelectTransformer() { 20 | @Override 21 | public Optional toSelect(SqlQuery sqlQuery) { 22 | return Optional.empty(); 23 | } 24 | }; 25 | 26 | Optional toSelect(SqlQuery sqlQuery); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/SelectTransformerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import net.sf.jsqlparser.JSQLParserException; 16 | import net.sf.jsqlparser.parser.CCJSqlParserUtil; 17 | import net.sf.jsqlparser.statement.Statement; 18 | import net.sf.jsqlparser.statement.delete.Delete; 19 | import net.sf.jsqlparser.statement.select.Select; 20 | import net.sf.jsqlparser.statement.update.Update; 21 | 22 | import java.util.Optional; 23 | 24 | class SelectTransformerFactory { 25 | 26 | private SelectTransformerFactory() { 27 | } 28 | 29 | private static final SelectTransformer SELECT_TO_SELECT_TRANSFORMER = 30 | 31 | new SelectTransformer() { 32 | @Override 33 | public Optional toSelect(SqlQuery sqlQuery) { 34 | return Optional.of(sqlQuery); 35 | } 36 | }; 37 | 38 | static SelectTransformer createSelectTransformer(SqlQuery sqlQuery) { 39 | 40 | String sqlQueryAsString = sqlQuery.getQueryAsString(); 41 | Statement statement = parse(sqlQueryAsString); 42 | 43 | if(statement instanceof Select) { 44 | return SELECT_TO_SELECT_TRANSFORMER; 45 | } 46 | if(statement instanceof Update) { 47 | Update update = (Update) statement; 48 | return new UpdateToSelectTransformer(update); 49 | } 50 | if(statement instanceof Delete) { 51 | Delete delete = (Delete) statement; 52 | return new DeleteToSelectTransformer(delete); 53 | } 54 | 55 | return SelectTransformer.NO_SELECT_TRANSFORMER; 56 | } 57 | 58 | private static Statement parse(String sqlQuery) { 59 | try { 60 | return CCJSqlParserUtil.parse(sqlQuery); 61 | } catch (JSQLParserException e) { 62 | e.printStackTrace(); 63 | return null; 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/SqlQuery.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import org.qstd.dbtype.DatabaseType; 16 | 17 | import java.util.*; 18 | 19 | import static java.util.stream.Collectors.joining; 20 | 21 | /** 22 | * Class to represent an SQL query 23 | */ 24 | public class SqlQuery { 25 | 26 | private final String queryAsString; 27 | 28 | private final List parameters; 29 | 30 | /** 31 | * Constructor to instantiate an SQL Query from an SQL query as String 32 | * @param queryAsString An SQL query as string 33 | */ 34 | public SqlQuery(String queryAsString) { 35 | this.queryAsString = queryAsString; 36 | this.parameters = Collections.emptyList(); 37 | } 38 | 39 | /** 40 | * Constructor to instantiate an SQL query from an SQL query with bind parameters and its bind parameter values 41 | * @param queryAsString An SQL query as String with bind parameters 42 | * @param parameters Bind parameter values 43 | */ 44 | public SqlQuery(String queryAsString, List parameters) { 45 | this.queryAsString = queryAsString; 46 | this.parameters = parameters; 47 | } 48 | 49 | static SqlQuery buildFromRow(DatasetRow rowToSearch, DatabaseType dbType) { 50 | Set columnNames = rowToSearch.getColumnNames(); 51 | return buildFromRow(columnNames, rowToSearch, dbType); 52 | 53 | } 54 | 55 | static SqlQuery buildFromRow(Collection columnNamesToSearch 56 | , DatasetRow rowToSearch 57 | , DatabaseType dbType) { 58 | Map valuesToMatch = rowToSearch.getColumnValueByColumnName(); 59 | String whereConditions = 60 | valuesToMatch.entrySet() 61 | .stream() 62 | .map(entry -> { 63 | String columnName = entry.getKey(); 64 | return columnName 65 | + (entry.getValue() == null 66 | ? " IS NULL" 67 | : "=" + ColumnValueFormatter.INSTANCE 68 | .formatColumnValue(entry.getValue(), dbType)); 69 | }) 70 | .collect(joining(" AND ")); 71 | String queryAsString = 72 | "SELECT " 73 | + String.join(", ", columnNamesToSearch) 74 | + " FROM " + rowToSearch.getTableName() 75 | + " WHERE " + whereConditions; 76 | return new SqlQuery(queryAsString); 77 | 78 | } 79 | 80 | @Override 81 | public String toString() { 82 | return getQueryAsString(); 83 | } 84 | 85 | String getQueryAsString() { 86 | return queryAsString; 87 | } 88 | 89 | List getParameters() { 90 | return parameters; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/UpdateToSelectTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd; 14 | 15 | import net.sf.jsqlparser.expression.Expression; 16 | import net.sf.jsqlparser.schema.Column; 17 | import net.sf.jsqlparser.statement.update.Update; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Collection; 21 | import java.util.List; 22 | import java.util.Optional; 23 | 24 | import static java.util.stream.Collectors.toList; 25 | 26 | class UpdateToSelectTransformer implements SelectTransformer { 27 | 28 | private Update updateStatement; 29 | 30 | UpdateToSelectTransformer(Update update) { 31 | updateStatement = update; 32 | } 33 | 34 | @Override 35 | public Optional toSelect(SqlQuery sqlQuery) { 36 | String tableName = updateStatement.getTable().getName(); 37 | String selectAsString = " SELECT " + findSelectedColumnsSeparatedWithCommas() 38 | + " FROM " + tableName 39 | + findWhereClauseIfExists(); 40 | List parameters = sqlQuery.getParameters(); 41 | SqlQuery selectQuery = new SqlQuery(selectAsString, parameters); 42 | return Optional.of(selectQuery); 43 | } 44 | 45 | private String findSelectedColumnsSeparatedWithCommas() { 46 | Collection columns = findColumnsToSelect(); 47 | return String.join(", ", columns); 48 | } 49 | 50 | private Collection findColumnsToSelect() { 51 | Collection columnNames = findUpdatedColumnNames(); 52 | Collection columnsToSelect = new ArrayList<>(columnNames); 53 | Collection whereColumnNames = findWhereColumnNames(); 54 | columnsToSelect.addAll(whereColumnNames); 55 | return columnsToSelect; 56 | } 57 | 58 | private List findUpdatedColumnNames() { 59 | return updateStatement 60 | .getColumns() 61 | .stream() 62 | .map(Column::getColumnName) 63 | .collect(toList()); 64 | } 65 | 66 | private String findWhereClauseIfExists() { 67 | Expression whereExpression = updateStatement.getWhere(); 68 | return whereExpression == null ? "" 69 | :" WHERE " + whereExpression; 70 | } 71 | 72 | private Collection findWhereColumnNames() { 73 | Expression whereExpression = updateStatement.getWhere(); 74 | return ColumnNamesExtractor.INSTANCE.findColumnNamesOf(whereExpression); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/BaseColumnOrdersFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.ColumnOrdersFinder; 16 | import org.qstd.PreparedStatementBuilder; 17 | import org.qstd.SqlQuery; 18 | 19 | import javax.sql.DataSource; 20 | import java.sql.Connection; 21 | import java.sql.PreparedStatement; 22 | import java.sql.ResultSet; 23 | import java.sql.SQLException; 24 | import java.util.ArrayList; 25 | import java.util.Collections; 26 | import java.util.List; 27 | 28 | class BaseColumnOrdersFinder implements ColumnOrdersFinder { 29 | 30 | private final DataSource dataSource; 31 | 32 | private final SqlQuery notNullColumnsQuery; 33 | 34 | BaseColumnOrdersFinder(DataSource dataSource, SqlQuery notNullColumnsQuery) { 35 | this.dataSource = dataSource; 36 | this.notNullColumnsQuery = notNullColumnsQuery; 37 | } 38 | 39 | @Override 40 | public List findDatabaseColumnOrdersOf(String tableName) { 41 | try (Connection connection = dataSource.getConnection(); 42 | PreparedStatement columnOrderStatement = PreparedStatementBuilder.buildFrom(notNullColumnsQuery, connection)) { 43 | columnOrderStatement.setString(1, tableName); 44 | ResultSet queryResult = columnOrderStatement.executeQuery(); 45 | return findColumnOrderFrom(queryResult); 46 | } catch (SQLException sqlException) { 47 | sqlException.printStackTrace(); 48 | } 49 | return Collections.emptyList(); 50 | } 51 | 52 | private List findColumnOrderFrom(ResultSet queryResult) throws SQLException { 53 | List columnOrder = new ArrayList<>(); 54 | while (queryResult.next()) { 55 | String columnName = queryResult.getString(3); 56 | columnOrder.add(columnName); 57 | } 58 | return columnOrder; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/BaseColumnsMappingsFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.*; 16 | 17 | import javax.sql.DataSource; 18 | import java.sql.Connection; 19 | import java.sql.PreparedStatement; 20 | import java.sql.ResultSet; 21 | import java.sql.SQLException; 22 | import java.util.ArrayList; 23 | import java.util.Collection; 24 | 25 | class BaseColumnsMappingsFinder implements ColumnsMappingsFinder { 26 | 27 | private final DataSource dataSource; 28 | 29 | private final SqlQuery columnsMappingQuery; 30 | 31 | public BaseColumnsMappingsFinder(DataSource dataSource, SqlQuery columnsMappingQuery) { 32 | this.dataSource = dataSource; 33 | this.columnsMappingQuery = columnsMappingQuery; 34 | } 35 | 36 | @Override 37 | public ColumnsMappingGroup findColumnsMappingsOf(String tableName) { 38 | 39 | Collection columnsMappings = new ArrayList<>(); 40 | 41 | try (Connection connection = dataSource.getConnection(); 42 | PreparedStatement referencedTablesStatement = PreparedStatementBuilder.buildFrom(columnsMappingQuery, connection)) { 43 | referencedTablesStatement.setString(1, tableName); 44 | 45 | ResultSet queryResult = referencedTablesStatement.executeQuery(); 46 | 47 | while (queryResult.next()) { 48 | ColumnsMapping columnsMapping = buildColumnsMappingFrom(queryResult); 49 | columnsMappings.add(columnsMapping); 50 | } 51 | 52 | 53 | } catch (SQLException sqlException) { 54 | sqlException.printStackTrace(); 55 | } 56 | 57 | 58 | return new ColumnsMappingGroup(columnsMappings); 59 | 60 | } 61 | 62 | private ColumnsMapping buildColumnsMappingFrom(ResultSet queryResult) throws SQLException { 63 | String firstTableSchema = queryResult.getString(1); 64 | String firstTableName = queryResult.getString(2); 65 | String firstTableColumn = queryResult.getString(3); 66 | 67 | ColumnMappingPart columnMappingPart1 68 | = new ColumnMappingPart(firstTableSchema, firstTableName, firstTableColumn); 69 | 70 | String secondTableSchema = queryResult.getString(4); 71 | String secondTableName = queryResult.getString(5); 72 | String secondTableColumn = queryResult.getString(6); 73 | 74 | ColumnMappingPart columnMappingPart2 75 | = new ColumnMappingPart(secondTableSchema, secondTableName, secondTableColumn); 76 | 77 | return new ColumnsMapping(columnMappingPart1, columnMappingPart2); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/BaseNotNullColumnsFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.NotNullColumnsFinder; 16 | import org.qstd.PreparedStatementBuilder; 17 | import org.qstd.SqlQuery; 18 | 19 | import javax.sql.DataSource; 20 | import java.sql.Connection; 21 | import java.sql.PreparedStatement; 22 | import java.sql.ResultSet; 23 | import java.sql.SQLException; 24 | import java.util.ArrayList; 25 | import java.util.Collection; 26 | import java.util.Collections; 27 | import java.util.List; 28 | 29 | public class BaseNotNullColumnsFinder implements NotNullColumnsFinder { 30 | 31 | private final DataSource dataSource; 32 | 33 | private final SqlQuery notNullColumnsQuery; 34 | 35 | BaseNotNullColumnsFinder(DataSource dataSource, SqlQuery notNullColumnsQuery) { 36 | this.dataSource = dataSource; 37 | this.notNullColumnsQuery = notNullColumnsQuery; 38 | } 39 | 40 | @Override 41 | public Collection findNotNullColumnsOf(String tableName) { 42 | try (Connection connection = dataSource.getConnection(); 43 | PreparedStatement columnOrderStatement = PreparedStatementBuilder.buildFrom(notNullColumnsQuery, connection)) { 44 | columnOrderStatement.setString(1, tableName); 45 | ResultSet queryResult = columnOrderStatement.executeQuery(); 46 | return findNotNullColumnsFrom(queryResult); 47 | } catch (SQLException sqlException) { 48 | sqlException.printStackTrace(); 49 | } 50 | return Collections.emptyList(); 51 | } 52 | 53 | private List findNotNullColumnsFrom(ResultSet resultSet) throws SQLException { 54 | List notNullColumns = new ArrayList<>(); 55 | while (resultSet.next()) { 56 | String columnName = resultSet.getString(3); 57 | notNullColumns.add(columnName); 58 | } 59 | return notNullColumns; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/BasePrimaryKeyColumnsFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.PreparedStatementBuilder; 16 | import org.qstd.PrimaryKeyColumnsFinder; 17 | import org.qstd.SqlQuery; 18 | 19 | import javax.sql.DataSource; 20 | import java.sql.Connection; 21 | import java.sql.PreparedStatement; 22 | import java.sql.ResultSet; 23 | import java.sql.SQLException; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | class BasePrimaryKeyColumnsFinder implements PrimaryKeyColumnsFinder { 28 | 29 | private final DataSource dataSource; 30 | 31 | private final SqlQuery primaryKeyColumnsQuery; 32 | 33 | public BasePrimaryKeyColumnsFinder(DataSource dataSource, SqlQuery primaryKeyColumnsQuery) { 34 | this.dataSource = dataSource; 35 | this.primaryKeyColumnsQuery = primaryKeyColumnsQuery; 36 | } 37 | 38 | @Override 39 | public List findPrimaryColumnsOf(String tableName) { 40 | 41 | List primaryKeyColumns = new ArrayList<>(); 42 | 43 | try (Connection connection = dataSource.getConnection(); 44 | PreparedStatement primaryKeyColumnsStatement = PreparedStatementBuilder.buildFrom(primaryKeyColumnsQuery, connection)) { 45 | 46 | primaryKeyColumnsStatement.setString(1, tableName); 47 | ResultSet queryResult = primaryKeyColumnsStatement.executeQuery(); 48 | 49 | while(queryResult.next()) { 50 | String column = queryResult.getString(4); 51 | primaryKeyColumns.add(column); 52 | } 53 | 54 | } catch (SQLException sqlException) { 55 | sqlException.printStackTrace(); 56 | } 57 | 58 | return primaryKeyColumns; 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/BaseReferencedTablesFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.*; 16 | 17 | import javax.sql.DataSource; 18 | import java.sql.Connection; 19 | import java.sql.PreparedStatement; 20 | import java.sql.ResultSet; 21 | import java.sql.SQLException; 22 | import java.util.ArrayList; 23 | import java.util.Collection; 24 | 25 | class BaseReferencedTablesFinder implements ReferencedTablesFinder { 26 | 27 | private final DataSource dataSource; 28 | 29 | private final SqlQuery referencedTableQuery; 30 | 31 | BaseReferencedTablesFinder(DataSource dataSource, SqlQuery referencedTableQuery) { 32 | this.dataSource = dataSource; 33 | this.referencedTableQuery = referencedTableQuery; 34 | } 35 | 36 | @Override 37 | public ReferencedTableSet findReferencedTablesOf(String tableName) { 38 | 39 | Collection referencedTables = new ArrayList<>(); 40 | try (Connection connection = dataSource.getConnection(); 41 | PreparedStatement referencedTablesStatement = PreparedStatementBuilder.buildFrom(referencedTableQuery, connection)) { 42 | 43 | referencedTablesStatement.setString(1, tableName); 44 | ResultSet queryResult = referencedTablesStatement.executeQuery(); 45 | 46 | while(queryResult.next()) { 47 | ReferencedTable referencedTable = buildReferencedTableFrom(queryResult); 48 | referencedTables.add(referencedTable); 49 | } 50 | 51 | } catch (SQLException sqlException) { 52 | sqlException.printStackTrace(); 53 | } 54 | return new ReferencedTableSet(referencedTables); 55 | } 56 | 57 | private ReferencedTable buildReferencedTableFrom(ResultSet queryResult) throws SQLException { 58 | String resultTableName = queryResult.getString(1); 59 | String referencedTableName = queryResult.getString(2); 60 | int level = queryResult.getInt(3); 61 | return new ReferencedTable(resultTableName 62 | , referencedTableName 63 | , level); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/DatabaseMetadataFinderFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.DatabaseMetadataFinder; 16 | 17 | import javax.sql.DataSource; 18 | 19 | import static org.qstd.dbtype.DatabaseType.*; 20 | 21 | /** 22 | * Factory to create an instance of {@link org.qstd.DatabaseMetadataFinder}. 23 | */ 24 | public class DatabaseMetadataFinderFactory { 25 | 26 | private DatabaseMetadataFinderFactory() { } 27 | 28 | /** 29 | * Creates a DatabaseMetadataFinder 30 | * @param dataSource A data source 31 | * @param dbType A database type 32 | * @return An instance of DatabaseMetadataFinder 33 | */ 34 | public static DatabaseMetadataFinder createDatabaseMetadataFinderFrom(DataSource dataSource, DatabaseType dbType) { 35 | 36 | if(dbType.equals(H2)) { 37 | return new H2MetadataFinder(dataSource); 38 | } 39 | 40 | if(dbType.equals(HSQLDB)) { 41 | return new HsqlDbMetadataFinder(dataSource); 42 | } 43 | 44 | if(dbType.equals(POSTGRE_SQL)) { 45 | return new PostgreSqlMetadataFinder(dataSource); 46 | } 47 | 48 | if (dbType.equals(MARIA_DB) || dbType.equals(MY_SQL)) { 49 | return new MariaDBMySQLMetadataFinder(dataSource); 50 | } 51 | 52 | if(dbType.equals(MICROSOFT_SQL_SERVER)) { 53 | return new MSSQLServerMetadataFinder(dataSource); 54 | } 55 | 56 | if(dbType.equals(ORACLE)) { 57 | return new OracleMetadataFinder(dataSource); 58 | } 59 | 60 | return new DefaultDatabaseMetadataFinder(dataSource); 61 | 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/DatabaseMetadataFinderWithCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.ColumnsMappingGroup; 16 | import org.qstd.DatabaseMetadataFinder; 17 | import org.qstd.ReferencedTableSet; 18 | 19 | import java.util.Collection; 20 | import java.util.List; 21 | import java.util.concurrent.ConcurrentHashMap; 22 | import java.util.function.Function; 23 | 24 | /** 25 | * A DatabaseMetadataFinder caching method calls 26 | */ 27 | public class DatabaseMetadataFinderWithCache implements DatabaseMetadataFinder { 28 | 29 | private final DatabaseMetadataFinder delegate; 30 | 31 | private final ConcurrentHashMap> notNullColumnsByTableName = new ConcurrentHashMap<>(); 32 | 33 | private final ConcurrentHashMap> databaseColumnOrdersByTableName = new ConcurrentHashMap<>(); 34 | 35 | private final ConcurrentHashMap columnsMappingsByTableName = new ConcurrentHashMap<>(); 36 | 37 | private final ConcurrentHashMap referencedTableSetByTableName = new ConcurrentHashMap<>(); 38 | 39 | private final ConcurrentHashMap> primaryColumnsByTableName = new ConcurrentHashMap<>(); 40 | 41 | public DatabaseMetadataFinderWithCache(DatabaseMetadataFinder delegate) { 42 | this.delegate = delegate; 43 | } 44 | 45 | public static DatabaseMetadataFinder buildFrom(DatabaseMetadataFinder databaseMetadataFinder) { 46 | return new DatabaseMetadataFinderWithCache(databaseMetadataFinder); 47 | } 48 | 49 | @Override 50 | public List findDatabaseColumnOrdersOf(String tableName) { 51 | return databaseColumnOrdersByTableName.computeIfAbsent(tableName, t -> delegate.findDatabaseColumnOrdersOf(tableName)); 52 | } 53 | 54 | @Override 55 | public ColumnsMappingGroup findColumnsMappingsOf(String tableName) { 56 | return columnsMappingsByTableName.computeIfAbsent(tableName, t -> delegate.findColumnsMappingsOf(tableName)); 57 | } 58 | 59 | @Override 60 | public Collection findNotNullColumnsOf(String tableName) { 61 | return notNullColumnsByTableName.computeIfAbsent(tableName, t -> delegate.findNotNullColumnsOf(tableName)); 62 | } 63 | 64 | @Override 65 | public ReferencedTableSet findReferencedTablesOf(String tableName) { 66 | return referencedTableSetByTableName.computeIfAbsent(tableName, t -> delegate.findReferencedTablesOf(tableName)); 67 | } 68 | 69 | @Override 70 | public List findPrimaryColumnsOf(String tableName) { 71 | return primaryColumnsByTableName.computeIfAbsent(tableName, t -> delegate.findPrimaryColumnsOf(tableName)); 72 | } 73 | 74 | @Override 75 | public Function getFunctionToHaveMetadataTableName() { 76 | return delegate.getFunctionToHaveMetadataTableName(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/DatabaseType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import static java.util.Arrays.stream; 16 | 17 | /** 18 | * Database type 19 | */ 20 | public enum DatabaseType { 21 | 22 | /**H2*/ 23 | H2("jdbc:h2") 24 | ,/**HSQLDB*/ 25 | HSQLDB("jdbc:hsqldb") 26 | ,/**MariaDB*/ 27 | MARIA_DB("jdbc:mariadb") 28 | ,/**Microsoft SQL Server*/ 29 | MICROSOFT_SQL_SERVER("jdbc:sqlserver") 30 | ,/**MySQL*/ 31 | MY_SQL("jdbc:mysql") 32 | ,/**Oracle*/ 33 | ORACLE("jdbc:oracle") 34 | ,/**PostgreSQL*/ 35 | POSTGRE_SQL("jdbc:postgresql") 36 | ,/**Other database type*/ 37 | OTHER("jdbc:"); 38 | 39 | private final String jdbcUrlStart; 40 | 41 | DatabaseType(String jdbcUrlStart) { 42 | this.jdbcUrlStart = jdbcUrlStart; 43 | } 44 | 45 | /** 46 | * Find the database type from the JDBC URL 47 | * @param jdbcUrl A JDBC URL 48 | * @return The database type 49 | */ 50 | public static DatabaseType findFromDbUrl(String jdbcUrl) { 51 | DatabaseType[] databaseTypes = DatabaseType.values(); 52 | return stream(databaseTypes) 53 | .filter(dbType -> dbType.accept(jdbcUrl)) 54 | .findFirst() 55 | .get(); 56 | } 57 | 58 | private boolean accept(String jdbcUrl) { 59 | return jdbcUrl.startsWith(jdbcUrlStart); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/DatabaseUrlFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import javax.sql.DataSource; 16 | import java.sql.Connection; 17 | import java.sql.DatabaseMetaData; 18 | import java.sql.SQLException; 19 | 20 | /** 21 | * Class helping to find database URL 22 | */ 23 | public class DatabaseUrlFinder { 24 | 25 | public static final DatabaseUrlFinder INSTANCE = new DatabaseUrlFinder(); 26 | 27 | /** 28 | * Find the database URL from a data source 29 | * @param dataSource A data source 30 | * @return The database URL 31 | */ 32 | public static String findDbUrlFrom(DataSource dataSource) { 33 | try (Connection connection = dataSource.getConnection()) { 34 | DatabaseMetaData metaData = connection.getMetaData(); 35 | return metaData.getURL().toLowerCase(); 36 | } catch (SQLException sqlException) { 37 | throw new IllegalStateException(sqlException); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/DefaultColumnOrdersFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.ColumnOrdersFinder; 16 | import org.qstd.SqlQuery; 17 | 18 | import javax.sql.DataSource; 19 | import java.util.List; 20 | 21 | class DefaultColumnOrdersFinder implements ColumnOrdersFinder { 22 | 23 | private static final SqlQuery COLUMN_ORDER_QUERY = new SqlQuery( 24 | " select table_schema," + 25 | " table_name," + 26 | " column_name," + 27 | " ordinal_position as position" + 28 | " from information_schema.columns" + 29 | " where table_name=?" + 30 | " order by position"); 31 | 32 | private BaseColumnOrdersFinder delegate; 33 | 34 | DefaultColumnOrdersFinder(DataSource dataSource) { 35 | delegate = new BaseColumnOrdersFinder(dataSource, COLUMN_ORDER_QUERY); 36 | } 37 | 38 | @Override 39 | public List findDatabaseColumnOrdersOf(String tableName) { 40 | return delegate.findDatabaseColumnOrdersOf(tableName); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/DefaultDatabaseMetadataFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.ColumnsMappingGroup; 16 | import org.qstd.DatabaseMetadataFinder; 17 | import org.qstd.ReferencedTableSet; 18 | 19 | import javax.sql.DataSource; 20 | import java.util.Collection; 21 | import java.util.Collections; 22 | import java.util.List; 23 | 24 | class DefaultDatabaseMetadataFinder implements DatabaseMetadataFinder { 25 | 26 | 27 | private final DefaultNotNullColumnsFinder defaultNotNullColumnsFinder; 28 | 29 | public DefaultDatabaseMetadataFinder(DataSource dataSource) { 30 | this.defaultNotNullColumnsFinder = new DefaultNotNullColumnsFinder(dataSource); 31 | } 32 | 33 | @Override 34 | public List findDatabaseColumnOrdersOf(String tableName) { 35 | return Collections.emptyList(); 36 | } 37 | 38 | @Override 39 | public ReferencedTableSet findReferencedTablesOf(String tableName) { 40 | return ReferencedTableSet.NONE; 41 | } 42 | 43 | @Override 44 | public Collection findNotNullColumnsOf(String tableName) { 45 | return defaultNotNullColumnsFinder.findNotNullColumnsOf(tableName); 46 | } 47 | 48 | @Override 49 | public ColumnsMappingGroup findColumnsMappingsOf(String tableName) { 50 | return ColumnsMappingGroup.NO_MAPPING; 51 | } 52 | 53 | @Override 54 | public List findPrimaryColumnsOf(String tableName) { 55 | return Collections.emptyList(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/DefaultNotNullColumnsFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.NotNullColumnsFinder; 16 | import org.qstd.SqlQuery; 17 | 18 | import javax.sql.DataSource; 19 | import java.util.Collection; 20 | 21 | class DefaultNotNullColumnsFinder implements NotNullColumnsFinder { 22 | 23 | private static final SqlQuery NOT_NULL_COLUMNS_QUERY = new SqlQuery( 24 | "select table_schema as table_schema,\n" + 25 | " table_name as table_name,\n" + 26 | " column_name as not_null_column\n" + 27 | "from information_schema.columns\n" + 28 | "where is_nullable = 'NO'\n" + 29 | " AND table_name=?"); 30 | 31 | private BaseNotNullColumnsFinder delegate; 32 | 33 | DefaultNotNullColumnsFinder(DataSource dataSource) { 34 | delegate = new BaseNotNullColumnsFinder(dataSource, NOT_NULL_COLUMNS_QUERY); 35 | } 36 | 37 | @Override 38 | public Collection findNotNullColumnsOf(String tableName) { 39 | return delegate.findNotNullColumnsOf(tableName); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/DefaultPrimaryKeyColumnsFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.PrimaryKeyColumnsFinder; 16 | import org.qstd.SqlQuery; 17 | 18 | import javax.sql.DataSource; 19 | import java.util.List; 20 | 21 | class DefaultPrimaryKeyColumnsFinder implements PrimaryKeyColumnsFinder { 22 | 23 | private static final SqlQuery PRIMARY_COLUMNS_QUERY = new SqlQuery( 24 | "select \n" + 25 | " cons.table_schema,\n" + 26 | " cons.table_name,\n" + 27 | " cons.constraint_name,\n" + 28 | " cols.column_name,\n" + 29 | " cols.ordinal_position as position\n" + 30 | " from information_schema.table_constraints cons\n" + 31 | " join information_schema.key_column_usage cols on (cons.table_schema = cols.table_schema \n" + 32 | " and cons.table_name = cols.table_name\n" + 33 | " and cons.constraint_name = cols.constraint_name)\n" + 34 | " where cons.table_name = ?" + "\n" + 35 | " and cons.constraint_type='PRIMARY KEY'" 36 | ); 37 | 38 | private final PrimaryKeyColumnsFinder delegate; 39 | 40 | DefaultPrimaryKeyColumnsFinder(DataSource dataSource) { 41 | delegate = new BasePrimaryKeyColumnsFinder(dataSource, PRIMARY_COLUMNS_QUERY); 42 | } 43 | 44 | @Override 45 | public List findPrimaryColumnsOf(String tableName) { 46 | return delegate.findPrimaryColumnsOf(tableName); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/H2MetadataFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.*; 16 | 17 | import javax.sql.DataSource; 18 | import java.util.Collection; 19 | import java.util.List; 20 | 21 | class H2MetadataFinder implements DatabaseMetadataFinder { 22 | 23 | private static final SqlQuery H2_REFERENCED_TABLES_QUERY = new SqlQuery( 24 | "with\n" + 25 | " recursive parent_child_tree (table_name, ref_table_name, level) as\n" + 26 | " (\n" + 27 | " select distinct\n" + 28 | " child.table_name as table_name,\n" + 29 | " parent.table_name as ref_table_name,\n" + 30 | " 1 as level\n" + 31 | " from information_schema.table_constraints child\n" + 32 | " join information_schema.referential_constraints rco\n" + 33 | " on rco.constraint_name = child.constraint_name\n" + 34 | " join information_schema.constraints parent\n" + 35 | " on parent.unique_index_name = rco.unique_constraint_name\n" + 36 | " where\n" + 37 | " child.table_name != parent.table_name and\n" + 38 | " child.table_name=?\n" + 39 | " UNION ALL\n" + 40 | " select pc.table_name, pc.ref_table_name, pct.level + 1 as level\n" + 41 | " from\n" + 42 | " (\n" + 43 | " select \n" + 44 | " child.table_name as table_name,\n" + 45 | " parent.table_name as ref_table_name,\n" + 46 | " 1 as level\n" + 47 | " from information_schema.table_constraints child\n" + 48 | " join information_schema.referential_constraints rco\n" + 49 | " on rco.constraint_name = child.constraint_name\n" + 50 | " join information_schema.constraints parent\n" + 51 | " on parent.unique_index_name = rco.unique_constraint_name\n" + 52 | " where\n" + 53 | " child.table_name != parent.table_name\n" + 54 | " ) pc\n" + 55 | " join parent_child_tree pct on (pc.table_name = pct.ref_table_name)\n" + 56 | " )\n" + 57 | "select distinct *\n" + 58 | "from parent_child_tree\n" + 59 | "order by level desc"); 60 | 61 | private static final SqlQuery H2_COLUMNS_MAPPINGS_QUERY = new SqlQuery( 62 | "select \n" + 63 | " fktable_schema as table_schema,\n" + 64 | " fktable_name as table_name,\n" + 65 | " fkcolumn_name as column_name,\n" + 66 | " pktable_schema as ref_table_schema,\n" + 67 | " pktable_name as ref_table_name,\n" + 68 | " pkcolumn_name as ref_column_name\n" + 69 | " from information_schema.cross_references \n" + 70 | " where fktable_name = ?" 71 | ); 72 | 73 | private final DefaultColumnOrdersFinder defaultColumnOrdersFinder; 74 | 75 | private final NotNullColumnsFinder defaultNotNullColumnsFinder; 76 | 77 | private final ReferencedTablesFinder h2ReferencedTablesFinder; 78 | 79 | private final ColumnsMappingsFinder h2ColumnsMappingsFinder; 80 | 81 | private final PrimaryKeyColumnsFinder primaryKeyColumnsFinder; 82 | 83 | H2MetadataFinder(DataSource dataSource) { 84 | this.defaultColumnOrdersFinder = new DefaultColumnOrdersFinder(dataSource); 85 | this.defaultNotNullColumnsFinder = new DefaultNotNullColumnsFinder(dataSource); 86 | this.h2ReferencedTablesFinder = new BaseReferencedTablesFinder(dataSource, H2_REFERENCED_TABLES_QUERY); 87 | this.h2ColumnsMappingsFinder = new BaseColumnsMappingsFinder(dataSource, H2_COLUMNS_MAPPINGS_QUERY); 88 | this.primaryKeyColumnsFinder = new DefaultPrimaryKeyColumnsFinder(dataSource); 89 | } 90 | 91 | @Override 92 | public List findDatabaseColumnOrdersOf(String tableName) { 93 | return defaultColumnOrdersFinder.findDatabaseColumnOrdersOf(tableName); 94 | } 95 | 96 | @Override 97 | public Collection findNotNullColumnsOf(String tableName) { 98 | return defaultNotNullColumnsFinder.findNotNullColumnsOf(tableName); 99 | } 100 | 101 | @Override 102 | public ReferencedTableSet findReferencedTablesOf(String tableName) { 103 | return h2ReferencedTablesFinder.findReferencedTablesOf(tableName); 104 | } 105 | 106 | @Override 107 | public ColumnsMappingGroup findColumnsMappingsOf(String tableName) { 108 | return h2ColumnsMappingsFinder.findColumnsMappingsOf(tableName); 109 | } 110 | 111 | @Override 112 | public List findPrimaryColumnsOf(String tableName) { 113 | return primaryKeyColumnsFinder.findPrimaryColumnsOf(tableName); 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/HsqlDbMetadataFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.*; 16 | 17 | import javax.sql.DataSource; 18 | import java.util.Collection; 19 | import java.util.List; 20 | 21 | class HsqlDbMetadataFinder implements DatabaseMetadataFinder { 22 | 23 | private static final SqlQuery HSQL_DB_REFERENCED_TABLES_QUERY = new SqlQuery( 24 | "with\n" + 25 | " recursive parent_child_tree (table_name, ref_table_name, level) as\n" + 26 | " (\n" + 27 | " select distinct\n" + 28 | " child.table_name as table_name,\n" + 29 | " parent.table_name as ref_table_name,\n" + 30 | " 1 as level\n" + 31 | " from information_schema.table_constraints child\n" + 32 | " join information_schema.referential_constraints rco\n" + 33 | " on rco.constraint_name = child.constraint_name\n" + 34 | " join information_schema.table_constraints parent\n" + 35 | " on parent.constraint_name = rco.unique_constraint_name\n" + 36 | " where\n" + 37 | " child.table_name != parent.table_name and\n" + 38 | " child.table_name=?\n" + 39 | " UNION\n" + 40 | " select pc.table_name, pc.ref_table_name, pct.level + 1 as level\n" + 41 | " from\n" + 42 | " (\n" + 43 | " select distinct\n" + 44 | " child.table_name as table_name,\n" + 45 | " parent.table_name as ref_table_name,\n" + 46 | " 1 as level\n" + 47 | " from information_schema.table_constraints child\n" + 48 | " join information_schema.referential_constraints rco\n" + 49 | " on rco.constraint_name = child.constraint_name\n" + 50 | " join information_schema.table_constraints parent\n" + 51 | " on parent.constraint_name = rco.unique_constraint_name\n" + 52 | " where\n" + 53 | " child.table_name != parent.table_name\n" + 54 | " ) pc\n" + 55 | " join parent_child_tree pct on (pc.table_name = pct.ref_table_name)\n" + 56 | " )\n" + 57 | "select distinct *\n" + 58 | "from parent_child_tree\n" + 59 | "order by level desc"); 60 | 61 | private static final SqlQuery HSQL_DB_COLUMNS_MAPPINGS_QUERY = new SqlQuery( 62 | "select\n" + 63 | " child_constraint.table_schema as table_schema,\n" + 64 | " child_constraint.table_name as table_name,\n" + 65 | " child_cons_cols.column_name as column_name,\n" + 66 | " parent_cons_cols.table_schema as ref_table_schema,\n" + 67 | " parent_cons_cols.table_name as ref_table_name,\n" + 68 | " parent_cons_cols.column_name as ref_column_name\n" + 69 | " from information_schema.table_constraints as child_constraint\n" + 70 | " join information_schema.key_column_usage as child_cons_cols\n" + 71 | " on (child_constraint.constraint_schema = child_cons_cols.constraint_schema\n" + 72 | " and\n" + 73 | " child_constraint.constraint_name = child_cons_cols.constraint_name\n" + 74 | " and\n" + 75 | " child_constraint.table_schema = child_cons_cols.table_schema)\n" + 76 | " join information_schema.referential_constraints as ref\n" + 77 | " on (child_constraint.constraint_schema = ref.constraint_schema\n" + 78 | " and\n" + 79 | " child_constraint.constraint_name = ref.constraint_name)\n" + 80 | " join information_schema.key_column_usage as parent_cons_cols\n" + 81 | " on (parent_cons_cols.constraint_schema = ref.unique_constraint_schema\n" + 82 | " and\n" + 83 | " parent_cons_cols.constraint_name = ref.unique_constraint_name)\n" + 84 | "where child_constraint.constraint_type = 'FOREIGN KEY' and child_constraint.table_name=?" 85 | ); 86 | 87 | private final DefaultColumnOrdersFinder defaultColumnOrdersFinder; 88 | 89 | private final NotNullColumnsFinder defaultNotNullColumnsFinder; 90 | 91 | private final ReferencedTablesFinder hsqlDbReferencedTablesFinder; 92 | 93 | private final ColumnsMappingsFinder hsqlDbColumnsMappingsFinder; 94 | 95 | private final DefaultPrimaryKeyColumnsFinder primaryKeyColumnsFinder; 96 | 97 | HsqlDbMetadataFinder(DataSource dataSource) { 98 | this.defaultColumnOrdersFinder = new DefaultColumnOrdersFinder(dataSource); 99 | this.defaultNotNullColumnsFinder = new DefaultNotNullColumnsFinder(dataSource); 100 | this.hsqlDbReferencedTablesFinder = new BaseReferencedTablesFinder(dataSource, HSQL_DB_REFERENCED_TABLES_QUERY); 101 | this.hsqlDbColumnsMappingsFinder = new BaseColumnsMappingsFinder(dataSource, HSQL_DB_COLUMNS_MAPPINGS_QUERY); 102 | this.primaryKeyColumnsFinder = new DefaultPrimaryKeyColumnsFinder(dataSource); 103 | } 104 | 105 | @Override 106 | public List findDatabaseColumnOrdersOf(String tableName) { 107 | return defaultColumnOrdersFinder.findDatabaseColumnOrdersOf(tableName); 108 | } 109 | 110 | @Override 111 | public Collection findNotNullColumnsOf(String tableName) { 112 | return defaultNotNullColumnsFinder.findNotNullColumnsOf(tableName); 113 | } 114 | 115 | @Override 116 | public ReferencedTableSet findReferencedTablesOf(String tableName) { 117 | return hsqlDbReferencedTablesFinder.findReferencedTablesOf(tableName); 118 | } 119 | 120 | @Override 121 | public ColumnsMappingGroup findColumnsMappingsOf(String tableName) { 122 | return hsqlDbColumnsMappingsFinder.findColumnsMappingsOf(tableName); 123 | } 124 | 125 | @Override 126 | public List findPrimaryColumnsOf(String tableName) { 127 | return primaryKeyColumnsFinder.findPrimaryColumnsOf(tableName); 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/MSSQLServerMetadataFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.*; 16 | 17 | import javax.sql.DataSource; 18 | import java.util.Collection; 19 | import java.util.Collections; 20 | import java.util.List; 21 | 22 | class MSSQLServerMetadataFinder implements DatabaseMetadataFinder { 23 | 24 | private static final SqlQuery MS_SQL_SERVER_REFERENCED_TABLES_QUERY = new SqlQuery( 25 | "with\n" + 26 | " parent_child_tree as\n" + 27 | " (\n" + 28 | " select distinct\n" + 29 | " child.table_name as table_name,\n" + 30 | " parent.table_name as ref_table_name,\n" + 31 | " 1 as level\n" + 32 | " from information_schema.referential_constraints rco\n" + 33 | " join information_schema.table_constraints child\n" + 34 | " on rco.constraint_name = child.constraint_name\n" + 35 | " and rco.constraint_schema = child.table_schema\n" + 36 | " join information_schema.table_constraints parent\n" + 37 | " on rco.unique_constraint_name = parent.constraint_name\n" + 38 | " and rco.unique_constraint_schema = parent.table_schema\n" + 39 | " where child.table_name != parent.table_name\n" + 40 | " and child.table_name=?\n" + 41 | " UNION ALL\n" + 42 | " select pc.table_name, pc.ref_table_name, pct.level + 1 as level\n" + 43 | " from\n" + 44 | " (\n" + 45 | " select \n" + 46 | " child.table_name as table_name,\n" + 47 | " parent.table_name as ref_table_name,\n" + 48 | " 1 as level\n" + 49 | " from information_schema.referential_constraints rco\n" + 50 | " join information_schema.table_constraints child\n" + 51 | " on rco.constraint_name = child.constraint_name\n" + 52 | " and rco.constraint_schema = child.table_schema\n" + 53 | " join information_schema.table_constraints parent\n" + 54 | " on rco.unique_constraint_name = parent.constraint_name\n" + 55 | " and rco.unique_constraint_schema = parent.table_schema\n" + 56 | " where child.table_name != parent.table_name\n" + 57 | " ) pc\n" + 58 | " join parent_child_tree pct on (pc.table_name = pct.ref_table_name)\n" + 59 | " )\n" + 60 | "select distinct *\n" + 61 | "from parent_child_tree\n" + 62 | "order by level desc"); 63 | 64 | private static final SqlQuery MS_SQL_SERVER_COLUMNS_MAPPINGS_QUERY = new SqlQuery( 65 | "select\n" + 66 | " child_constraint.table_schema as table_schema,\n" + 67 | " child_constraint.table_name as table_name,\n" + 68 | " child_cons_cols.column_name as column_name,\n" + 69 | " parent_cons_cols.table_schema as ref_table_schema,\n" + 70 | " parent_cons_cols.table_name as ref_table_name,\n" + 71 | " parent_cons_cols.column_name as ref_column_name\n" + 72 | " from information_schema.table_constraints as child_constraint\n" + 73 | " join information_schema.key_column_usage as child_cons_cols\n" + 74 | " on (child_constraint.constraint_schema = child_cons_cols.constraint_schema\n" + 75 | " and\n" + 76 | " child_constraint.constraint_name = child_cons_cols.constraint_name\n" + 77 | " and\n" + 78 | " child_constraint.table_schema = child_cons_cols.table_schema)\n" + 79 | " join information_schema.referential_constraints as ref\n" + 80 | " on (child_constraint.constraint_schema = ref.constraint_schema\n" + 81 | " and\n" + 82 | " child_constraint.constraint_name = ref.constraint_name)\n" + 83 | " join information_schema.key_column_usage as parent_cons_cols\n" + 84 | " on (parent_cons_cols.constraint_schema = ref.unique_constraint_schema\n" + 85 | " and\n" + 86 | " parent_cons_cols.constraint_name = ref.unique_constraint_name)\n" + 87 | "where child_constraint.constraint_type = 'FOREIGN KEY' and child_constraint.table_name=?"); 88 | 89 | private final DefaultColumnOrdersFinder defaultColumnOrdersFinder; 90 | 91 | private final NotNullColumnsFinder defaultNotNullColumnsFinder; 92 | 93 | private final ReferencedTablesFinder mssqlServerReferencedTablesFinder; 94 | 95 | private final ColumnsMappingsFinder mssqlServerColumnsMappingsFinder; 96 | 97 | MSSQLServerMetadataFinder(DataSource dataSource) { 98 | this.defaultColumnOrdersFinder = new DefaultColumnOrdersFinder(dataSource); 99 | this.defaultNotNullColumnsFinder = new DefaultNotNullColumnsFinder(dataSource); 100 | this.mssqlServerReferencedTablesFinder = new BaseReferencedTablesFinder(dataSource, MS_SQL_SERVER_REFERENCED_TABLES_QUERY); 101 | this.mssqlServerColumnsMappingsFinder = new BaseColumnsMappingsFinder(dataSource, MS_SQL_SERVER_COLUMNS_MAPPINGS_QUERY); 102 | } 103 | 104 | @Override 105 | public List findDatabaseColumnOrdersOf(String tableName) { 106 | return defaultColumnOrdersFinder.findDatabaseColumnOrdersOf(tableName); 107 | } 108 | 109 | @Override 110 | public Collection findNotNullColumnsOf(String tableName) { 111 | return defaultNotNullColumnsFinder.findNotNullColumnsOf(tableName); 112 | } 113 | 114 | @Override 115 | public ReferencedTableSet findReferencedTablesOf(String tableName) { 116 | return mssqlServerReferencedTablesFinder.findReferencedTablesOf(tableName); 117 | } 118 | 119 | @Override 120 | public ColumnsMappingGroup findColumnsMappingsOf(String tableName) { 121 | return mssqlServerColumnsMappingsFinder.findColumnsMappingsOf(tableName); 122 | } 123 | 124 | @Override 125 | public List findPrimaryColumnsOf(String tableName) { 126 | return Collections.emptyList(); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/MariaDBMySQLMetadataFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.*; 16 | 17 | import javax.sql.DataSource; 18 | import java.util.Collection; 19 | import java.util.Collections; 20 | import java.util.List; 21 | 22 | class MariaDBMySQLMetadataFinder implements DatabaseMetadataFinder { 23 | 24 | private static final SqlQuery MARIA_DB_MY_SQL_COLUMNS_MAPPINGS_QUERY 25 | = new SqlQuery("select\n" + 26 | " child_constraint.table_schema as table_schema,\n" + 27 | " child_constraint.table_name as table_name,\n" + 28 | " child_cons_cols.column_name as column_name,\n" + 29 | " child_cons_cols.referenced_table_schema as ref_table_schema,\n" + 30 | " child_cons_cols.referenced_table_name as ref_table_name,\n" + 31 | " child_cons_cols.referenced_column_name as ref_column_name\n" + 32 | " from information_schema.table_constraints as child_constraint\n" + 33 | " join information_schema.key_column_usage as child_cons_cols\n" + 34 | " on (child_constraint.constraint_schema = child_cons_cols.constraint_schema\n" + 35 | " and\n" + 36 | " child_constraint.constraint_name = child_cons_cols.constraint_name\n" + 37 | " and\n" + 38 | " child_constraint.table_schema = child_cons_cols.table_schema\n" + 39 | " and\n" + 40 | " child_constraint.table_name = child_cons_cols.table_name)\n" + 41 | "where child_constraint.constraint_type = 'FOREIGN KEY' and child_constraint.table_name=?"); 42 | 43 | private final DefaultColumnOrdersFinder defaultColumnOrdersFinder; 44 | 45 | private final NotNullColumnsFinder defaultNotNullColumnsFinder; 46 | 47 | private final PostgreSqlMariaDbReferencedTablesFinder postgreSqlMariaDbReferencedTablesFinder; 48 | 49 | private final BaseColumnsMappingsFinder mariaDbMySqlColumnsMappingsFinder; 50 | 51 | private final PrimaryKeyColumnsFinder primaryKeyColumnsFinder; 52 | 53 | MariaDBMySQLMetadataFinder(DataSource dataSource) { 54 | this.defaultColumnOrdersFinder = new DefaultColumnOrdersFinder(dataSource); 55 | this.defaultNotNullColumnsFinder = new DefaultNotNullColumnsFinder(dataSource); 56 | this.postgreSqlMariaDbReferencedTablesFinder = new PostgreSqlMariaDbReferencedTablesFinder(dataSource); 57 | this.mariaDbMySqlColumnsMappingsFinder = new BaseColumnsMappingsFinder(dataSource, MARIA_DB_MY_SQL_COLUMNS_MAPPINGS_QUERY); 58 | this.primaryKeyColumnsFinder = new DefaultPrimaryKeyColumnsFinder(dataSource); 59 | } 60 | 61 | @Override 62 | public List findDatabaseColumnOrdersOf(String tableName) { 63 | return defaultColumnOrdersFinder.findDatabaseColumnOrdersOf(tableName); 64 | } 65 | 66 | @Override 67 | public Collection findNotNullColumnsOf(String tableName) { 68 | return defaultNotNullColumnsFinder.findNotNullColumnsOf(tableName); 69 | } 70 | 71 | @Override 72 | public ReferencedTableSet findReferencedTablesOf(String tableName) { 73 | return postgreSqlMariaDbReferencedTablesFinder.findReferencedTablesOf(tableName); 74 | } 75 | 76 | @Override 77 | public ColumnsMappingGroup findColumnsMappingsOf(String tableName) { 78 | return mariaDbMySqlColumnsMappingsFinder.findColumnsMappingsOf(tableName); 79 | } 80 | 81 | @Override 82 | public List findPrimaryColumnsOf(String tableName) { 83 | return Collections.emptyList(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/OracleMetadataFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.*; 16 | 17 | import javax.sql.DataSource; 18 | import java.util.Collection; 19 | import java.util.List; 20 | import java.util.function.Function; 21 | 22 | public class OracleMetadataFinder implements DatabaseMetadataFinder { 23 | 24 | private static final SqlQuery COLUMNS_ORDER_QUERY = new SqlQuery( 25 | "select owner as table_schema," + 26 | " table_name as table_name," + 27 | " column_name as column_name," + 28 | " column_id as position" + 29 | " from all_tab_columns\n" + 30 | " where table_name = ?" + 31 | " order by position" 32 | ); 33 | 34 | private static final SqlQuery NOT_NULL_COLUMNS_QUERY = new SqlQuery( 35 | "select owner as table_schema," + 36 | " table_name as table_name," + 37 | " column_name as mandatory_column" + 38 | " from all_tab_columns" + 39 | " where table_name = ?" + 40 | " and nullable = 'N'" 41 | ); 42 | 43 | private static final SqlQuery REFERENCED_TABLES_QUERY = new SqlQuery( 44 | "select\n" + 45 | " table_name,\n" + 46 | " ref_table_name,\n" + 47 | " level\n" + 48 | " from\n" + 49 | " (\n" + 50 | " select\n" + 51 | " c.owner as table_schema,\n" + 52 | " c.table_name,\n" + 53 | " c.r_owner as ref_table_schema,\n" + 54 | " ref_c.table_name as ref_table_name\n" + 55 | " from\n" + 56 | " all_constraints c\n" + 57 | " inner join all_constraints ref_c on ref_c.constraint_name = c.r_constraint_name\n" + 58 | " where\n" + 59 | " c.constraint_type = 'R'\n" + 60 | " and c.table_name != ref_c.table_name\n" + 61 | " )\n" + 62 | " start with table_name = ?\n" + 63 | " connect by table_name = prior ref_table_name\n" + 64 | "order by level desc" 65 | ); 66 | 67 | private static final SqlQuery COLUMNS_MAPPING_QUERY = new SqlQuery( 68 | "select\n" + 69 | " c.owner as table_schema,\n" + 70 | " c.table_name,\n" + 71 | " col.column_name,\n" + 72 | " c.r_owner as ref_table_schema,\n" + 73 | " ref_col.table_name as ref_table_name,\n" + 74 | " ref_col.column_name as ref_column_name\n" + 75 | " from\n" + 76 | " all_constraints c\n" + 77 | " inner join all_cons_columns col on col.owner = c.owner\n" + 78 | " and col.constraint_name = c.constraint_name\n" + 79 | " inner join all_cons_columns ref_col on ref_col.owner = c.r_owner\n" + 80 | " and ref_col.constraint_name = c.r_constraint_name\n" + 81 | " and ref_col.position = col.position\n" + 82 | " where \n" + 83 | " c.table_name = ?\n" + 84 | " and c.constraint_type = 'R'"); 85 | 86 | private static final SqlQuery PRIMARY_KEY_QUERY = new SqlQuery( 87 | "select\n" + 88 | " c.owner as table_schema,\n" + 89 | " c.table_name,\n" + 90 | " c.constraint_name,\n" + 91 | " col.column_name,\n" + 92 | " col.position\n" + 93 | " from\n" + 94 | " all_constraints c\n" + 95 | " inner join all_cons_columns col on col.owner = c.owner\n" + 96 | " and col.constraint_name = c.constraint_name\n" + 97 | " where c.table_name = ?\n" + 98 | " and c.constraint_type = 'P'\n" + 99 | " order by position"); 100 | 101 | private final BaseColumnOrdersFinder columnOrdersFinder; 102 | 103 | private final NotNullColumnsFinder notNullColumnsFinder; 104 | 105 | private final ReferencedTablesFinder referencedTablesFinder; 106 | 107 | private final BaseColumnsMappingsFinder columnsMappingsFinder; 108 | 109 | private final PrimaryKeyColumnsFinder primaryKeyColumnsFinder; 110 | 111 | @Override 112 | public Function getFunctionToHaveMetadataTableName() { 113 | return tableName -> tableName.toUpperCase(); 114 | } 115 | 116 | OracleMetadataFinder(DataSource dataSource) { 117 | columnOrdersFinder = new BaseColumnOrdersFinder(dataSource, COLUMNS_ORDER_QUERY); 118 | notNullColumnsFinder = new BaseNotNullColumnsFinder(dataSource, NOT_NULL_COLUMNS_QUERY); 119 | referencedTablesFinder = new BaseReferencedTablesFinder(dataSource, REFERENCED_TABLES_QUERY); 120 | columnsMappingsFinder = new BaseColumnsMappingsFinder(dataSource, COLUMNS_MAPPING_QUERY); 121 | primaryKeyColumnsFinder = new BasePrimaryKeyColumnsFinder(dataSource, PRIMARY_KEY_QUERY); 122 | } 123 | 124 | @Override 125 | public List findDatabaseColumnOrdersOf(String tableName) { 126 | return columnOrdersFinder.findDatabaseColumnOrdersOf(tableName); 127 | } 128 | 129 | @Override 130 | public ColumnsMappingGroup findColumnsMappingsOf(String tableName) { 131 | return columnsMappingsFinder.findColumnsMappingsOf(tableName); 132 | } 133 | 134 | @Override 135 | public Collection findNotNullColumnsOf(String tableName) { 136 | return notNullColumnsFinder.findNotNullColumnsOf(tableName); 137 | } 138 | 139 | @Override 140 | public List findPrimaryColumnsOf(String tableName) { 141 | return primaryKeyColumnsFinder.findPrimaryColumnsOf(tableName); 142 | } 143 | 144 | @Override 145 | public ReferencedTableSet findReferencedTablesOf(String tableName) { 146 | return referencedTablesFinder.findReferencedTablesOf(tableName); 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/PostgreSqlMariaDbReferencedTablesFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.ReferencedTableSet; 16 | import org.qstd.ReferencedTablesFinder; 17 | import org.qstd.SqlQuery; 18 | 19 | import javax.sql.DataSource; 20 | 21 | class PostgreSqlMariaDbReferencedTablesFinder implements ReferencedTablesFinder { 22 | 23 | private static final SqlQuery REFERENCED_TABLES_QUERY = new SqlQuery("with \n" + 24 | " recursive parent_child_tree as\n" + 25 | " (\n" + 26 | " with parent_child as\n" + 27 | " (\n" + 28 | " select distinct\n" + 29 | " child.table_schema as table_schema,\n" + 30 | " child.table_name as table_name,\n" + 31 | " parent.table_schema as ref_table_schema,\n" + 32 | " parent.table_name as ref_table_name\n" + 33 | " from information_schema.referential_constraints rco\n" + 34 | " join information_schema.table_constraints child\n" + 35 | " on rco.constraint_name = child.constraint_name\n" + 36 | " and rco.constraint_schema = child.table_schema\n" + 37 | " join information_schema.table_constraints parent\n" + 38 | " on rco.unique_constraint_name = parent.constraint_name\n" + 39 | " and rco.unique_constraint_schema = parent.table_schema\n" + 40 | " where child.table_name != parent.table_name\n" + 41 | " )\n" + 42 | " select table_name, ref_table_name, 1 as level\n" + 43 | " from parent_child\n" + 44 | " where table_name=?\n" + 45 | " UNION\n" + 46 | " select pc.table_name, pc.ref_table_name, pct.level + 1 as level\n" + 47 | " from parent_child_tree pct\n" + 48 | " join parent_child pc on (pc.table_name = pct.ref_table_name)\n" + 49 | " )\n" + 50 | "select *\n" + 51 | "from parent_child_tree\n" + 52 | "order by level desc"); 53 | 54 | private final BaseReferencedTablesFinder referencedTablesFinder; 55 | 56 | PostgreSqlMariaDbReferencedTablesFinder(DataSource dataSource) { 57 | this.referencedTablesFinder = new BaseReferencedTablesFinder(dataSource, REFERENCED_TABLES_QUERY); 58 | } 59 | 60 | @Override 61 | public ReferencedTableSet findReferencedTablesOf(String tableName) { 62 | return referencedTablesFinder.findReferencedTablesOf(tableName); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/qstd/dbtype/PostgreSqlMetadataFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.dbtype; 14 | 15 | import org.qstd.*; 16 | 17 | import javax.sql.DataSource; 18 | import java.util.Collection; 19 | import java.util.List; 20 | 21 | class PostgreSqlMetadataFinder implements DatabaseMetadataFinder { 22 | 23 | private static final SqlQuery POSTGRE_SQL_COLUMNS_MAPPINGS_QUERY = new SqlQuery("select\n" + 24 | " tc.table_schema as table_schema,\n" + 25 | " tc.table_name as table_name,\n" + 26 | " kcu.column_name as column_name,\n" + 27 | " ccu.table_schema as ref_table_schema,\n" + 28 | " ccu.table_name as ref_table_name,\n" + 29 | " ccu.column_name as ref_column_name\n" + 30 | " from information_schema.table_constraints as tc\n" + 31 | " join information_schema.key_column_usage as kcu\n" + 32 | " using (constraint_schema, constraint_name, table_schema)\n" + 33 | " join information_schema.constraint_column_usage as ccu\n" + 34 | " using (constraint_schema, constraint_name, table_schema)\n" + 35 | "where tc.constraint_type = 'FOREIGN KEY' and tc.table_name=?"); 36 | 37 | private final DefaultColumnOrdersFinder defaultColumnOrdersFinder; 38 | 39 | private final NotNullColumnsFinder defaultNotNullColumnsFinder; 40 | 41 | private final PostgreSqlMariaDbReferencedTablesFinder postgreSqlMariaDbReferencedTablesFinder; 42 | 43 | private final ColumnsMappingsFinder postgreSqlColumnsMappingsFinder; 44 | 45 | private final PrimaryKeyColumnsFinder primaryKeyColumnsFinder; 46 | 47 | PostgreSqlMetadataFinder(DataSource dataSource) { 48 | this.defaultColumnOrdersFinder = new DefaultColumnOrdersFinder(dataSource); 49 | this.defaultNotNullColumnsFinder = new DefaultNotNullColumnsFinder(dataSource); 50 | this.postgreSqlMariaDbReferencedTablesFinder = new PostgreSqlMariaDbReferencedTablesFinder(dataSource); 51 | this.postgreSqlColumnsMappingsFinder = new BaseColumnsMappingsFinder(dataSource, POSTGRE_SQL_COLUMNS_MAPPINGS_QUERY); 52 | this.primaryKeyColumnsFinder = new DefaultPrimaryKeyColumnsFinder(dataSource); 53 | } 54 | 55 | @Override 56 | public List findDatabaseColumnOrdersOf(String tableName) { 57 | return defaultColumnOrdersFinder.findDatabaseColumnOrdersOf(tableName); 58 | } 59 | 60 | @Override 61 | public Collection findNotNullColumnsOf(String tableName) { 62 | return defaultNotNullColumnsFinder.findNotNullColumnsOf(tableName); 63 | } 64 | 65 | @Override 66 | public ReferencedTableSet findReferencedTablesOf(String tableName) { 67 | return postgreSqlMariaDbReferencedTablesFinder.findReferencedTablesOf(tableName); 68 | } 69 | 70 | @Override 71 | public ColumnsMappingGroup findColumnsMappingsOf(String tableName) { 72 | return postgreSqlColumnsMappingsFinder.findColumnsMappingsOf(tableName); 73 | } 74 | 75 | @Override 76 | public List findPrimaryColumnsOf(String tableName) { 77 | return primaryKeyColumnsFinder.findPrimaryColumnsOf(tableName); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/DataSourceBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import com.zaxxer.hikari.HikariConfig; 16 | import com.zaxxer.hikari.HikariDataSource; 17 | 18 | import javax.sql.DataSource; 19 | 20 | class DataSourceBuilder { 21 | 22 | static final DataSourceBuilder INSTANCE = new DataSourceBuilder(); 23 | 24 | private DataSourceBuilder() { } 25 | 26 | static DataSource build(String jdbcUrl, String dbUserName, String dbPassword) { 27 | HikariConfig hikariConfig = new HikariConfig(); 28 | hikariConfig.setJdbcUrl(jdbcUrl); 29 | hikariConfig.setUsername(dbUserName); 30 | hikariConfig.setPassword(dbPassword); 31 | return new HikariDataSource(hikariConfig); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/DatasetRowApiTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.jupiter.api.Test; 16 | import org.qstd.DatasetRow; 17 | import org.qstd.QuickSqlTestData; 18 | 19 | import java.util.List; 20 | 21 | import static org.qstd.test.TestTable.TestTableAssert.assertThat; 22 | import static org.qstd.test.TestTable.buildUniqueTable; 23 | 24 | public class DatasetRowApiTest extends H2Config { 25 | 26 | @Test public void 27 | should_generate_working_insert_from_a_dataset_row() { 28 | 29 | // GIVEN 30 | TestTable playerTable = 31 | buildUniqueTable(DATA_SOURCE 32 | , "Player" 33 | , " id bigint" 34 | + ", firstName varchar(255)" 35 | + ", lastName varchar(255)") 36 | .create() 37 | .insertValues("1, 'Paul', 'Pogba'"); 38 | 39 | // WHEN 40 | DatasetRow datasetRow = 41 | DatasetRow.ofTable(playerTable.getTableName()) 42 | .addColumnValue("id", 1) 43 | .addColumnValue("firstName", "Paul") 44 | .addColumnValue("lastName", "Pogba"); 45 | 46 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 47 | List insertStatements = quickSqlTestData.generateInsertListFor(datasetRow); 48 | 49 | // THEN 50 | playerTable.recreate(); 51 | SQL_EXECUTOR.execute(insertStatements); 52 | assertThat(playerTable).withGeneratedInserts(insertStatements) 53 | .hasNumberOfRows(1) 54 | .row(0).hasValues(1, "Paul", "Pogba"); 55 | 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/DatasetRowsMergingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.jupiter.api.Test; 16 | import org.qstd.QuickSqlTestData; 17 | 18 | import java.util.Random; 19 | 20 | import static org.qstd.test.TestTable.TestTableAssert.assertThat; 21 | 22 | public class DatasetRowsMergingTest extends H2Config { 23 | 24 | @Test public void 25 | should_merge_dataset_rows_if_columns_in_common_have_same_values() { 26 | 27 | // GIVEN 28 | TestTable table = 29 | TestTable.buildUniqueTable(DATA_SOURCE 30 | , "Table" 31 | , "col1 varchar(255)" 32 | + ", col2 varchar(255)" 33 | + ", col3 varchar(255)" 34 | ) 35 | .create() 36 | .insertValues("'val1', 'val2', 'val3'"); 37 | 38 | String tableName = table.getTableName(); 39 | String select1 = "SELECT col1, col2 FROM " + tableName; 40 | String select2 = "SELECT col1, col3 FROM " + tableName; 41 | 42 | // WHEN 43 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 44 | String insertScript = quickSqlTestData.generateInsertScriptFor(select1, select2); 45 | 46 | // THEN 47 | table.recreate(); 48 | SQL_EXECUTOR.execute(insertScript); 49 | assertThat(table).withScript(insertScript) 50 | .hasNumberOfRows(1) 51 | .row(0).hasValues("val1", "val2", "val3"); 52 | 53 | } 54 | 55 | @Test public void 56 | should_merge_dataset_rows_in_case_of_joined_rows() { 57 | 58 | // GIVEN 59 | TestTable sponsorTable = 60 | TestTable.buildUniqueTable(DATA_SOURCE 61 | , "Sponsor" 62 | , " id bigint" + 63 | ", name varchar(255) not null" + 64 | ", country varchar(255)" + 65 | ", primary key (id)" 66 | ) 67 | .create() 68 | .insertValues("1, 'Sponsor name', 'France'"); 69 | 70 | String teamSponsorForeignKey = "add constraint team_sponsor_fk" + generateRandomPositiveInt() 71 | + " foreign key (sponsor_id)" 72 | + " references " + sponsorTable.getTableName(); 73 | 74 | TestTable teamTable = 75 | TestTable.buildUniqueTable(DATA_SOURCE 76 | , "Team" 77 | ," id bigint not null" + 78 | ", name varchar(255) not null" + 79 | ", sponsor_id bigint not null" + 80 | ", primary key (id)" 81 | ) 82 | .create() 83 | .alter(teamSponsorForeignKey) 84 | .insertValues("1, 'Manchester United', 1"); 85 | 86 | String playerTeamForeignKey = "add constraint player_team_fk" + generateRandomPositiveInt() 87 | + " foreign key (team_id)" 88 | + " references " + teamTable.getTableName(); 89 | TestTable playerTable = 90 | TestTable.buildUniqueTable(DATA_SOURCE 91 | , "Player" 92 | , "id bigint not null" 93 | + ", firstName varchar(255)" 94 | + ", lastName varchar(255)" 95 | + ", team_id bigint not null" 96 | + ", primary key (id)" 97 | ) 98 | .create() 99 | .alter(playerTeamForeignKey) 100 | .insertValues("1, 'Paul', 'Pogba', 1"); 101 | 102 | String playerSelect = "SELECT * FROM " + playerTable.getTableName(); 103 | String sponsorSelect = "SELECT * FROM " + sponsorTable.getTableName(); 104 | 105 | // WHEN 106 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 107 | String insertScript = quickSqlTestData.generateInsertScriptFor(playerSelect, sponsorSelect); 108 | 109 | // THEN 110 | playerTable.drop(); 111 | teamTable.drop(); 112 | sponsorTable.drop().create(); 113 | teamTable.create().alter(teamSponsorForeignKey); 114 | playerTable.create().alter(playerTeamForeignKey); 115 | SQL_EXECUTOR.execute(insertScript); 116 | 117 | assertThat(sponsorTable).withScript(insertScript) 118 | .hasNumberOfRows(1) 119 | .row(0).hasValues(1, "Sponsor name", "France"); 120 | 121 | } 122 | 123 | private int generateRandomPositiveInt() { 124 | Random random = new Random(); 125 | return Math.abs(random.nextInt()); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/DeleteTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.jupiter.api.Test; 16 | import org.qstd.QuickSqlTestData; 17 | 18 | import static org.qstd.test.TestTable.TestTableAssert.assertThat; 19 | import static org.qstd.test.TestTable.buildUniqueTable; 20 | 21 | public class DeleteTest extends H2Config { 22 | 23 | @Test 24 | public void 25 | should_generate_insert_if_all_rows_are_deleted_and_no_mandatory_columns() { 26 | 27 | // GIVEN 28 | TestTable table1 = 29 | buildUniqueTable(DATA_SOURCE 30 | , "Table_1" 31 | , " id bigint" 32 | + ", Col_A varchar(255)" 33 | + ", Col_B varchar(255)" 34 | + ", Col_dec decimal") 35 | .create() 36 | .insertValues("1, 'A1', 'B1', 1.80") 37 | .insertValues("2, 'A2', 'B2', 2.99"); 38 | 39 | String deleteQuery = "DELETE FROM " + table1.getTableName(); 40 | 41 | // WHEN 42 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 43 | String insertScript = quickSqlTestData.generateInsertScriptFor(deleteQuery); 44 | 45 | // THEN 46 | table1.recreate(); 47 | SQL_EXECUTOR.execute(insertScript); 48 | assertThat(table1).withScript(insertScript) 49 | .hasNumberOfRows(2) 50 | .row(0).hasValues(1, "A1", "B1", 1.80) 51 | .row(1).hasValues(2, "A2", "B2", 2.99); 52 | } 53 | 54 | @Test 55 | public void 56 | should_generate_one_insert_if_one_rows_is_deleted() { 57 | 58 | // GIVEN 59 | TestTable table1 = 60 | buildUniqueTable(DATA_SOURCE 61 | , "Table_1" 62 | , " id bigint" 63 | + ", Col_A varchar(255)" 64 | + ", Col_B varchar(255)" 65 | + ", Col_dec decimal") 66 | .create() 67 | .insertValues("1, 'A1', 'B1', 1.80") 68 | .insertValues("2, 'A2', 'B2', 2.99"); 69 | 70 | String updateQuery = "DELETE FROM " + table1.getTableName() 71 | + " WHERE Col_A = 'A1'"; 72 | 73 | // WHEN 74 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 75 | String insertScript = quickSqlTestData.generateInsertScriptFor(updateQuery); 76 | 77 | // THEN 78 | table1.recreate(); 79 | SQL_EXECUTOR.execute(insertScript); 80 | assertThat(table1).withScript(insertScript) 81 | .hasNumberOfRows(1) 82 | .row(0).hasValues(1, "A1", "B1", 1.80); 83 | 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/FastTestSuite.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.platform.runner.JUnitPlatform; 16 | import org.junit.platform.suite.api.SelectClasses; 17 | import org.junit.platform.suite.api.SuiteDisplayName; 18 | import org.junit.runner.RunWith; 19 | 20 | @RunWith(JUnitPlatform.class) 21 | @SuiteDisplayName("Fast tests") 22 | @SelectClasses( { H2Test.class 23 | , NotFullyManagedDatabaseTest.class 24 | , DatasetRowApiTest.class 25 | , SelectTest.class 26 | , UpdateTest.class 27 | , DeleteTest.class 28 | , InsertTest.class 29 | , H2DateTypesTest.class 30 | , SortInsertStatementsTest.class 31 | , SortInsertStatementsWithPkTest.class 32 | , DatasetRowsMergingTest.class 33 | , JdbcRoundtripTest.class} ) 34 | public class FastTestSuite { 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/H2Config.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.jupiter.api.BeforeAll; 16 | import org.quickperf.junit5.QuickPerfTest; 17 | 18 | import javax.sql.DataSource; 19 | 20 | import static org.quickperf.sql.config.QuickPerfSqlDataSourceBuilder.aDataSourceBuilder; 21 | 22 | @QuickPerfTest 23 | class H2Config { 24 | 25 | static DataSource DATA_SOURCE; 26 | 27 | static SqlExecutor SQL_EXECUTOR; 28 | 29 | @BeforeAll 30 | public static void beforeAll() { 31 | DataSource h2Datasource = DataSourceBuilder.build("jdbc:h2:mem:test", "user", "pwd"); 32 | DATA_SOURCE = aDataSourceBuilder().buildProxy(h2Datasource); 33 | SQL_EXECUTOR = new SqlExecutor(DATA_SOURCE); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/H2DateTypesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.assertj.core.api.Assertions; 16 | import org.junit.jupiter.api.Test; 17 | import org.qstd.QuickSqlTestData; 18 | 19 | import static org.qstd.test.TestTable.TestTableAssert.assertThat; 20 | import static org.qstd.test.TestTable.buildUniqueTable; 21 | 22 | public class H2DateTypesTest extends H2Config { 23 | 24 | @Test public void 25 | should_generate_an_insert_statement_with_a_date_type() { 26 | 27 | // GIVEN 28 | TestTable playerTable = 29 | buildUniqueTable(DATA_SOURCE 30 | , "Table" 31 | , "date Date" 32 | ) 33 | .create() 34 | .insertValues("'2012-09-17'"); 35 | 36 | // WHEN 37 | String playerTableName = playerTable.getTableName(); 38 | String select = "SELECT * FROM " + playerTableName; 39 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 40 | String insertScript = quickSqlTestData.generateInsertScriptFor(select); 41 | 42 | // THEN 43 | playerTable.recreate(); 44 | SQL_EXECUTOR.execute(insertScript); 45 | assertThat(playerTable).withScript(insertScript) 46 | .hasNumberOfRows(1) 47 | .row(0).hasValues("2012-09-17"); 48 | 49 | } 50 | 51 | @Test public void 52 | should_generate_an_insert_statement_with_a_timestamp_type() { 53 | 54 | // GIVEN 55 | TestTable playerTable = 56 | buildUniqueTable(DATA_SOURCE 57 | , "Table" 58 | , "timestampCol TIMESTAMP" 59 | ) 60 | .create() 61 | .insertValues("'2012-09-17 19:56:47.32'"); 62 | 63 | // WHEN 64 | String playerTableName = playerTable.getTableName(); 65 | String select = "SELECT * FROM " + playerTableName; 66 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 67 | String insertScript = quickSqlTestData.generateInsertScriptFor(select); 68 | 69 | // THEN 70 | playerTable.recreate(); 71 | SQL_EXECUTOR.execute(insertScript); 72 | assertThat(playerTable).withScript(insertScript) 73 | .hasNumberOfRows(1); 74 | Assertions.assertThat(insertScript).contains("'2012-09-17 19:56:47.32'"); 75 | 76 | } 77 | 78 | @Test public void 79 | should_generate_an_insert_statement_with_a_timestamp_with_time_zone_type() { 80 | 81 | // GIVEN 82 | TestTable playerTable = 83 | buildUniqueTable(DATA_SOURCE 84 | , "Table" 85 | , "col TIMESTAMP WITH TIME ZONE" 86 | ) 87 | .create() 88 | .insertValues("'2012-09-17 19:56:47.32 UTC'"); 89 | 90 | // WHEN 91 | String playerTableName = playerTable.getTableName(); 92 | String select = "SELECT * FROM " + playerTableName; 93 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 94 | String insertScript = quickSqlTestData.generateInsertScriptFor(select); 95 | 96 | // THEN 97 | playerTable.recreate(); 98 | SQL_EXECUTOR.execute(insertScript); 99 | assertThat(playerTable).withScript(insertScript) 100 | .hasNumberOfRows(1); 101 | 102 | } 103 | 104 | @Test public void 105 | should_generate_an_insert_statement_with_a_time_type() { 106 | 107 | // GIVEN 108 | TestTable playerTable = 109 | buildUniqueTable(DATA_SOURCE 110 | , "Table" 111 | , "col TIME" 112 | ) 113 | .create() 114 | .insertValues("'23:59:59'"); 115 | 116 | // WHEN 117 | String playerTableName = playerTable.getTableName(); 118 | String select = "SELECT * FROM " + playerTableName; 119 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 120 | String insertScript = quickSqlTestData.generateInsertScriptFor(select); 121 | 122 | // THEN 123 | playerTable.recreate(); 124 | SQL_EXECUTOR.execute(insertScript); 125 | assertThat(playerTable).withScript(insertScript) 126 | .hasNumberOfRows(1) 127 | .row(0).hasValues("23:59:59"); 128 | 129 | } 130 | 131 | @Test public void 132 | should_generate_an_insert_statement_with_a_time_with_timezone_type() { 133 | 134 | // GIVEN 135 | TestTable playerTable = 136 | buildUniqueTable(DATA_SOURCE 137 | , "Table" 138 | , "col TIME WITH TIME ZONE" 139 | ) 140 | .create() 141 | .insertValues("'23:59:59 UTC'"); 142 | 143 | // WHEN 144 | String playerTableName = playerTable.getTableName(); 145 | String select = "SELECT * FROM " + playerTableName; 146 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 147 | String insertScript = quickSqlTestData.generateInsertScriptFor(select); 148 | 149 | // THEN 150 | playerTable.recreate(); 151 | SQL_EXECUTOR.execute(insertScript); 152 | assertThat(playerTable).withScript(insertScript) 153 | .hasNumberOfRows(1); 154 | 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/InsertTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.jupiter.api.Test; 16 | import org.quickperf.sql.annotation.ExpectJdbcQueryExecution; 17 | import org.qstd.QuickSqlTestData; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | public class InsertTest extends H2Config { 22 | 23 | @Test public void 24 | should_generate_an_empty_insert_script_for_an_insert_input() { 25 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 26 | String anInsertStatement = "INSERT INTO A_TABLE VALUES(1, 2, 3)"; 27 | String insertScript = quickSqlTestData.generateInsertScriptFor(anInsertStatement); 28 | assertThat(insertScript).isEmpty(); 29 | } 30 | 31 | @Test @ExpectJdbcQueryExecution(0) public void 32 | should_not_use_jdbc_execution_for_an_insert_input() { 33 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 34 | String anInsertStatement = "INSERT INTO A_TABLE VALUES(1, 2, 3)"; 35 | quickSqlTestData.generateInsertScriptFor(anInsertStatement); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/JdbcRoundtripTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.jupiter.api.AfterEach; 16 | import org.junit.jupiter.api.BeforeEach; 17 | import org.junit.jupiter.api.Test; 18 | import org.quickperf.sql.annotation.ExpectJdbcQueryExecution; 19 | import org.qstd.QuickSqlTestData; 20 | 21 | import java.util.Random; 22 | 23 | import static org.qstd.test.TestTable.TestTableAssert.assertThat; 24 | 25 | public class JdbcRoundtripTest extends H2Config { 26 | 27 | private TestTable t1Table; 28 | private TestTable t2Table; 29 | private TestTable t3Table; 30 | private String t2TableConstraint; 31 | private String insertScript; 32 | 33 | @BeforeEach 34 | public void prepare_test_data() { 35 | 36 | t1Table = 37 | TestTable.buildUniqueTable(DATA_SOURCE 38 | , "t1" 39 | , "id_t1 bigint not null" + 40 | ", c2_t1 varchar(255) not null" + 41 | ", c3_t1 varchar(255) not null" + 42 | ", c4_t1 varchar(255) not null" + 43 | ", primary key (id_t1)" 44 | ) 45 | .create() 46 | .insertValues("1, 't1_r1_c1_val', 't1_r1_c2_val', 't1_r1_c3_val'") 47 | .insertValues("2, 't1_r2_c1_val', 't1_r2_c2_val', 't1_r2_c3_val'"); 48 | 49 | t2TableConstraint = "add constraint t1_t2_fk" + generateRandomPositiveInt() 50 | + " foreign key (t1_id)" 51 | + " references " + t1Table.getTableName(); 52 | 53 | t2Table = 54 | TestTable.buildUniqueTable(DATA_SOURCE 55 | , "t2" 56 | , "id_t2 bigint not null" + 57 | ", t1_id bigint not null" + 58 | ", c1_t2 varchar(255) not null" + 59 | ", c2_t2 varchar(255) not null" + 60 | ", c3_t2 varchar(255) not null" + 61 | ", primary key (id_t2)") 62 | .create() 63 | .alter(t2TableConstraint) 64 | .insertValues("1, 1, 't2_r1_c1_val', 't2_r1_c2_val', 't2_r1_c3_val'") 65 | .insertValues("2, 2, 't2_r2_c1_val', 't2_r2_c2_val', 't2_r2_c3_val'"); 66 | 67 | t3Table = 68 | TestTable.buildUniqueTable(DATA_SOURCE 69 | , "t3" 70 | , "id_t3 bigint not null" + 71 | ", c2_t3 varchar(255) not null" + 72 | ", c3_t3 varchar(255) not null" + 73 | ", c4_t3 varchar(255) not null" + 74 | ", primary key (id_t3)") 75 | .create() 76 | .insertValues("1, 't3_r1_c1_val', 't3_r1_c2_val', 't3_r1_c3_val'") 77 | .insertValues("2, 't3_r2_c1_val', 't3_r2_c2_val', 't3_r2_c3_val'"); 78 | 79 | } 80 | 81 | private int generateRandomPositiveInt() { 82 | Random random = new Random(); 83 | return Math.abs(random.nextInt()); 84 | } 85 | 86 | @ExpectJdbcQueryExecution(23) 87 | @Test public void 88 | should_limit_jdbc_roundtrips() { 89 | String t2Select = "SELECT c1_t2 FROM " + t2Table.getTableName(); 90 | String t3Select = "SELECT c2_t3 FROM " + t3Table.getTableName(); 91 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 92 | insertScript = quickSqlTestData.generateInsertScriptFor(t2Select, t3Select); 93 | } 94 | 95 | @AfterEach 96 | public void check_inserted_data() { 97 | t3Table.recreate(); 98 | t2Table.drop(); 99 | t1Table.drop().create(); 100 | t2Table.create().alter(t2TableConstraint); 101 | SQL_EXECUTOR.execute(insertScript); 102 | assertThat(t1Table).withScript(insertScript) 103 | .hasNumberOfRows(2); 104 | assertThat(t2Table).withScript(insertScript) 105 | .hasNumberOfRows(2); 106 | assertThat(t3Table).withScript(insertScript) 107 | .hasNumberOfRows(2); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/MariaDBSlowTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.jupiter.api.BeforeAll; 16 | import org.junit.jupiter.api.Test; 17 | import org.qstd.QuickSqlTestData; 18 | import org.testcontainers.containers.MariaDBContainer; 19 | 20 | import javax.sql.DataSource; 21 | import java.util.List; 22 | import java.util.Random; 23 | 24 | import static org.qstd.test.TestTable.TestTableAssert.assertThat; 25 | import static org.qstd.test.TestTable.buildUniqueTable; 26 | 27 | public class MariaDBSlowTest { 28 | 29 | private static final String DB_USER_NAME = "user"; 30 | 31 | private static final String DB_PASSWORD = "pwd"; 32 | 33 | private static final MariaDBContainer MARIA_DB_CONTAINER 34 | = new MariaDBContainer<>("mariadb:10.5.2") 35 | .withDatabaseName("mariadb") 36 | .withUsername(DB_USER_NAME) 37 | .withPassword(DB_PASSWORD); 38 | 39 | private static DataSource DATA_SOURCE; 40 | 41 | private static SqlExecutor SQL_EXECUTOR; 42 | 43 | @BeforeAll 44 | public static void beforeAll() { 45 | MARIA_DB_CONTAINER.start(); 46 | String jdbcUrl = MARIA_DB_CONTAINER.getJdbcUrl(); 47 | DATA_SOURCE = DataSourceBuilder.INSTANCE.build(jdbcUrl, DB_USER_NAME, DB_PASSWORD); 48 | SQL_EXECUTOR = new SqlExecutor(DATA_SOURCE); 49 | } 50 | 51 | @Test public void 52 | should_sort_insert_statements_following_table_dependencies() { 53 | 54 | // GIVEN 55 | TestTable teamTable = 56 | buildUniqueTable(DATA_SOURCE 57 | , "Team" 58 | ," id bigint not null" + 59 | ", name varchar(255)" + 60 | ", primary key (id)") 61 | .create() 62 | .insertValues("1, 'Manchester United'"); 63 | 64 | String playerTableConstraint = "add constraint player_team_fk" + generateRandomPositiveInt() 65 | + " foreign key (team_id)" 66 | + " references " + teamTable.getTableName() + " (id)"; 67 | TestTable playerTable = 68 | buildUniqueTable(DATA_SOURCE 69 | , "Player" 70 | , "id bigint not null" 71 | + ", firstName varchar(255)" 72 | + ", lastName varchar(255)" 73 | + ", team_id bigint" 74 | + ", primary key (id)") 75 | .create() 76 | .alter(playerTableConstraint) 77 | .insertValues("1, 'Paul', 'Pogba', 1"); 78 | 79 | // WHEN 80 | String playerSelect = "SELECT * FROM " + playerTable.getTableName(); 81 | String teamSelect = "SELECT * FROM " + teamTable.getTableName(); 82 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 83 | List insertStatements = quickSqlTestData.generateInsertListFor(playerSelect, teamSelect); 84 | 85 | // THEN 86 | playerTable.drop(); 87 | teamTable.drop().create(); 88 | playerTable.create().alter(playerTableConstraint); 89 | SQL_EXECUTOR.execute(insertStatements); 90 | assertThat(playerTable).withGeneratedInserts(insertStatements) 91 | .hasNumberOfRows(1); 92 | assertThat(teamTable).withGeneratedInserts(insertStatements) 93 | .hasNumberOfRows(1); 94 | 95 | } 96 | 97 | private int generateRandomPositiveInt() { 98 | Random random = new Random(); 99 | return Math.abs(random.nextInt()); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/NotFullyManagedDatabaseTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.jupiter.api.Test; 16 | import org.qstd.DatabaseMetadataFinder; 17 | import org.qstd.QuickSqlTestData; 18 | import org.qstd.dbtype.DatabaseType; 19 | 20 | import static org.qstd.dbtype.DatabaseMetadataFinderFactory.createDatabaseMetadataFinderFrom; 21 | import static org.qstd.test.TestTable.*; 22 | import static org.qstd.test.TestTable.TestTableAssert.assertThat; 23 | 24 | public class NotFullyManagedDatabaseTest extends H2Config { 25 | 26 | @Test public void 27 | should_generate_insert_statements_without_necessarily_taking_account_of_database_constraints_if_the_database_is_not_supposed_to_be_fully_managed() { 28 | 29 | // GIVEN 30 | TestTable table = 31 | buildUniqueTable(DATA_SOURCE 32 | , "Table" 33 | , "col1 varchar(25)" 34 | + ", col2 varchar(25)" 35 | + ", col3 varchar(25)" 36 | ) 37 | .create() 38 | .insertValues("'val1', 'val2', 'val3'") 39 | .insertValues("'val3', 'val4', 'val5'"); 40 | 41 | DatabaseMetadataFinder databaseMetadataFinderOfNotFullyManagedDatabase = 42 | createDatabaseMetadataFinderFrom(DATA_SOURCE, DatabaseType.OTHER); 43 | QuickSqlTestData quickSqlTestDataOfNotFullyManagedDatabase = 44 | QuickSqlTestData.buildFrom(DATA_SOURCE, DatabaseType.OTHER 45 | , databaseMetadataFinderOfNotFullyManagedDatabase); 46 | 47 | String select = "SELECT col1, col2 FROM " + table.getTableName(); 48 | 49 | // WHEN 50 | String insertScript = quickSqlTestDataOfNotFullyManagedDatabase.generateInsertScriptFor(select); 51 | 52 | // THEN 53 | table.recreate(); 54 | SQL_EXECUTOR.execute(insertScript); 55 | assertThat(table).withScript(insertScript) 56 | .hasNumberOfRows(2); 57 | 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/SelectTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.jupiter.api.Test; 16 | import org.qstd.QuickSqlTestData; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | import static org.qstd.test.TestTable.*; 22 | import static org.qstd.test.TestTable.TestTableAssert.*; 23 | 24 | public class SelectTest extends H2Config { 25 | 26 | @Test public void 27 | should_generate_working_insert_from_a_select_statement() { 28 | 29 | // GIVEN 30 | TestTable playerTable = 31 | buildUniqueTable(DATA_SOURCE 32 | , "Player" 33 | , " id bigint" 34 | + ", firstName varchar(255)" 35 | + ", lastName varchar(255)" 36 | ) 37 | .create() 38 | .insertValues("1, 'Paul', 'Pogba'") 39 | .insertValues("2, 'Antoine', 'Griezmann'"); 40 | 41 | // WHEN 42 | String playerTableName = playerTable.getTableName(); 43 | String select = "SELECT * FROM " + playerTableName; 44 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 45 | String insertScript = quickSqlTestData.generateInsertScriptFor(select); 46 | 47 | // THEN 48 | playerTable.recreate(); 49 | SQL_EXECUTOR.execute(insertScript); 50 | assertThat(playerTable).withScript(insertScript) 51 | .hasNumberOfRows(2) 52 | .row(0).hasValues(1, "Paul", "Pogba") 53 | .row(1).hasValues(2, "Antoine", "Griezmann"); 54 | 55 | } 56 | 57 | @Test public void 58 | should_generate_an_insert_statement_from_a_select_containing_bind_parameters() { 59 | 60 | // GIVEN 61 | TestTable table = 62 | buildUniqueTable(DATA_SOURCE 63 | , "Table" 64 | , "col1 varchar(25)" 65 | + ", col2 varchar(25)" 66 | + ", col3 varchar(25)" 67 | ) 68 | .create() 69 | .insertValues("'val1', 'val2', 'val3'"); 70 | 71 | String tableName = table.getTableName(); 72 | String select = " SELECT col1, col2, col3 FROM " + tableName 73 | + " WHERE col2=? AND col3=?"; 74 | 75 | List parameterValues = Arrays.asList("val2", "val3"); 76 | 77 | // WHEN 78 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 79 | String insertScript = quickSqlTestData.generateInsertScriptFor(select, parameterValues); 80 | 81 | // THEN 82 | table.recreate(); 83 | SQL_EXECUTOR.execute(insertScript); 84 | assertThat(table).withScript(insertScript) 85 | .hasNumberOfRows(1) 86 | .row(0).hasValues("val1", "val2", "val3"); 87 | 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/SortInsertStatementsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.jupiter.api.RepeatedTest; 16 | import org.qstd.QuickSqlTestData; 17 | 18 | import java.util.List; 19 | import java.util.Random; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.qstd.test.TestTable.TestTableAssert.assertThat; 23 | import static org.qstd.test.TestTable.buildUniqueTable; 24 | 25 | public class SortInsertStatementsTest extends H2Config { 26 | 27 | @RepeatedTest(9) public void 28 | should_sort_insert_statements_following_table_dependencies() { 29 | 30 | // GIVEN 31 | TestTable teamTable = 32 | buildUniqueTable(DATA_SOURCE 33 | , "Team" 34 | ," id bigint not null" + 35 | ", name varchar(255)" + 36 | ", primary key (id)" 37 | ) 38 | .create() 39 | .insertValues("1, 'Manchester United'"); 40 | 41 | String playerTableConstraint = "add constraint player_team_fk" + generateRandomPositiveInt() 42 | + " foreign key (team_id)" 43 | + " references " + teamTable.getTableName(); 44 | TestTable playerTable = 45 | buildUniqueTable(DATA_SOURCE 46 | , "Player" 47 | , "id bigint not null" 48 | + ", firstName varchar(255)" 49 | + ", lastName varchar(255)" 50 | + ", team_id bigint" 51 | + ", primary key (id)" 52 | ) 53 | .create() 54 | .alter(playerTableConstraint) 55 | .insertValues("1, 'Paul', 'Pogba', 1"); 56 | 57 | String playerSelect = "SELECT * FROM " + playerTable.getTableName(); 58 | String teamSelect = "SELECT * FROM " + teamTable.getTableName(); 59 | 60 | // WHEN 61 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 62 | String insertScript = quickSqlTestData.generateInsertScriptFor(playerSelect, teamSelect); 63 | 64 | // THEN 65 | playerTable.drop(); 66 | teamTable.drop().create(); 67 | playerTable.create().alter(playerTableConstraint); 68 | SQL_EXECUTOR.execute(insertScript); 69 | assertThat(playerTable).withScript(insertScript) 70 | .hasNumberOfRows(1); 71 | assertThat(teamTable).withScript(insertScript) 72 | .hasNumberOfRows(1); 73 | 74 | } 75 | 76 | private int generateRandomPositiveInt() { 77 | Random random = new Random(); 78 | return Math.abs(random.nextInt()); 79 | } 80 | 81 | @RepeatedTest(9) public void 82 | should_sort_insert_statements_following_table_names_if_independent_tables() { 83 | 84 | TestTable testTable1 = 85 | buildUniqueTable(DATA_SOURCE 86 | , "TABLE_1" 87 | , "col varchar(20)" 88 | ) 89 | .create() 90 | .insertValues("'value_col_tab1'"); 91 | 92 | TestTable testTable2 = 93 | buildUniqueTable(DATA_SOURCE 94 | , "TABLE_2" 95 | , "col varchar(20)" 96 | ) 97 | .create() 98 | .insertValues("'value_col_tab2'"); 99 | 100 | String tab1Select = "SELECT * FROM " + testTable1.getTableName(); 101 | String tab2Select = "SELECT * FROM " + testTable2.getTableName(); 102 | 103 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 104 | List insertStatements = quickSqlTestData.generateInsertListFor(tab2Select, tab1Select); 105 | 106 | assertThat(insertStatements.get(0)).contains(testTable1.getTableName()); 107 | assertThat(insertStatements.get(1)).contains(testTable2.getTableName()); 108 | 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/SqlExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import javax.sql.DataSource; 16 | import java.sql.Connection; 17 | import java.sql.PreparedStatement; 18 | import java.sql.SQLException; 19 | import java.util.List; 20 | 21 | public class SqlExecutor { 22 | 23 | private final DataSource dataSource; 24 | 25 | SqlExecutor(DataSource dataSource) { 26 | this.dataSource = dataSource; 27 | } 28 | 29 | void execute(List queries) { 30 | for (String query : queries) { 31 | execute(query); 32 | } 33 | } 34 | 35 | void execute(String sql) { 36 | try (Connection connection = dataSource.getConnection(); 37 | PreparedStatement statement = connection.prepareStatement(sql)) { 38 | statement.execute(); 39 | } catch (SQLException e) { 40 | throw new IllegalStateException("Unable to execute " + System.lineSeparator() + sql, e); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/TestTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.assertj.core.api.AbstractAssert; 16 | import org.assertj.db.api.Assertions; 17 | import org.assertj.db.api.TableAssert; 18 | import org.assertj.db.api.TableRowAssert; 19 | import org.assertj.db.type.Table; 20 | 21 | import javax.sql.DataSource; 22 | import java.util.List; 23 | import java.util.Random; 24 | 25 | class TestTable { 26 | 27 | private final String tableName; 28 | 29 | private final String creationScript; 30 | 31 | private final DataSource dataSource; 32 | 33 | private final SqlExecutor sqlExecutor; 34 | 35 | TestTable(DataSource dataSource, String tableName, String creationScript) { 36 | this.tableName = tableName; 37 | this.creationScript = creationScript; 38 | this.dataSource = dataSource; 39 | this.sqlExecutor = new SqlExecutor(dataSource); 40 | } 41 | 42 | static TestTable buildUniqueTable(DataSource dataSource 43 | , String baseTableName 44 | , String colDescsAndConstraints) { 45 | String tableName = buildUniqueTableName(baseTableName); 46 | String creationScript = "create table " + tableName 47 | + "(" + colDescsAndConstraints + ")"; 48 | return new TestTable(dataSource, tableName, creationScript); 49 | } 50 | 51 | private static String buildUniqueTableName(String baseTableName) { 52 | return baseTableName + "_" + generateRandomPositiveInt(); 53 | } 54 | 55 | private static int generateRandomPositiveInt() { 56 | Random random = new Random(); 57 | return Math.abs(random.nextInt()); 58 | } 59 | 60 | TestTable recreate() { 61 | drop(); 62 | create(); 63 | return this; 64 | } 65 | 66 | TestTable drop() { 67 | sqlExecutor.execute("drop table " + tableName); 68 | return this; 69 | } 70 | 71 | TestTable create() { 72 | sqlExecutor.execute(creationScript); 73 | return this; 74 | } 75 | 76 | TestTable insertValues(String valuesSeparatedWithCommas) { 77 | String insert = "INSERT INTO " + tableName + " VALUES (" +valuesSeparatedWithCommas + ")"; 78 | sqlExecutor.execute(insert); 79 | return this; 80 | } 81 | 82 | String getTableName() { 83 | return tableName; 84 | } 85 | 86 | TestTable alter(String alterCode) { 87 | sqlExecutor.execute("alter table " + tableName + " " + alterCode); 88 | return this; 89 | } 90 | 91 | static class TestTableAssert extends AbstractAssert { 92 | 93 | private static final String LINE_SEPARATOR = System.lineSeparator(); 94 | private Table assertJDbTable; 95 | 96 | TestTableAssert(TestTable testTable, Class selfType) { 97 | super(testTable, TestTableAssert.class); 98 | } 99 | 100 | static TestTableAssert assertThat(TestTable testTable) { 101 | TestTableAssert testTableAssert = new TestTableAssert(testTable, TestTableAssert.class); 102 | testTableAssert.assertJDbTable = new Table(testTable.dataSource, testTable.tableName); 103 | return testTableAssert; 104 | } 105 | 106 | TableAssert hasNumberOfRows(int expected) { 107 | return Assertions.assertThat(assertJDbTable).hasNumberOfRows(expected); 108 | } 109 | 110 | TableAssert withScript(String sqlScript) { 111 | String description = LINE_SEPARATOR 112 | + "SQL script: " 113 | + LINE_SEPARATOR 114 | + sqlScript 115 | + LINE_SEPARATOR; 116 | return Assertions.assertThat(assertJDbTable).as(description); 117 | } 118 | 119 | TableRowAssert row(int index) { 120 | return Assertions.assertThat(assertJDbTable).row(index); 121 | } 122 | 123 | TableAssert withGeneratedInserts(List generatedInsertStatements) { 124 | String description = LINE_SEPARATOR 125 | + "Queries: " 126 | + LINE_SEPARATOR 127 | + String.join(LINE_SEPARATOR, generatedInsertStatements); 128 | return Assertions.assertThat(assertJDbTable).as(description); 129 | } 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/test/java/org/qstd/test/UpdateTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 3 | * the License. You may obtain a copy of the License at 4 | * 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 8 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * Copyright 2021-2022 the original author or authors. 12 | */ 13 | package org.qstd.test; 14 | 15 | import org.junit.jupiter.api.Test; 16 | import org.qstd.QuickSqlTestData; 17 | 18 | import static org.qstd.test.TestTable.TestTableAssert.assertThat; 19 | import static org.qstd.test.TestTable.buildUniqueTable; 20 | 21 | public class UpdateTest extends H2Config { 22 | 23 | @Test public void 24 | should_generate_one_insert_if_all_rows_are_updated_and_no_mandatory_columns() { 25 | 26 | // GIVEN 27 | TestTable foodTable = 28 | buildUniqueTable(DATA_SOURCE 29 | , "Food" 30 | , " id bigint" 31 | + ", Dishname varchar(255)" 32 | + ", Allergy varchar(255)" 33 | + ", Price decimal") 34 | .create() 35 | .insertValues("1, 'Spaghetti Bolognese', 'cheese', 6.80") 36 | .insertValues("2, 'Pizza Margherita', 'pasta', 7.99"); 37 | 38 | // WHEN 39 | String foodTableName = foodTable.getTableName(); 40 | String updateQuery = "UPDATE " + foodTableName 41 | + " SET Price = 7.00, Allergy = 'none'"; 42 | 43 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 44 | String insertScript = quickSqlTestData.generateInsertScriptFor(updateQuery); 45 | 46 | // THEN 47 | foodTable.recreate(); 48 | SQL_EXECUTOR.execute(insertScript); 49 | assertThat(foodTable).withScript(insertScript) 50 | .hasNumberOfRows(2) 51 | .row(0).hasValues(null, null, "cheese", 6.80); 52 | 53 | } 54 | 55 | @Test public void 56 | should_generate_insert_statements_from_update_containing_where_or_like() { 57 | 58 | // GIVEN 59 | TestTable foodTable = 60 | buildUniqueTable(DATA_SOURCE 61 | , "Food" 62 | , " id bigint" 63 | + ", Dishname varchar(255)" 64 | + ", Allergy varchar(255)" 65 | + ", Price decimal") 66 | .create() 67 | .insertValues("1, 'Spaghetti Bolognese', 'cheese', 6.80") 68 | .insertValues("2, 'Pizza', 'pasta', 10.99"); 69 | 70 | // WHEN 71 | String foodTableName = foodTable.getTableName(); 72 | String updateQuery = "UPDATE " + foodTableName + " SET Price = 7.00" 73 | + " WHERE Allergy LIKE 'past%' OR Allergy = 'cheese'" 74 | + " OR Dishname = 'Pizza'"; 75 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 76 | String insertScript = quickSqlTestData.generateInsertScriptFor(updateQuery); 77 | 78 | // THEN 79 | foodTable.recreate(); 80 | SQL_EXECUTOR.execute(insertScript); 81 | assertThat(foodTable).withScript(insertScript) 82 | .hasNumberOfRows(2) 83 | .column(0).hasOnlyNullValues() 84 | .column(1).containsValues("Spaghetti Bolognese", "Pizza") 85 | .column(2).containsValues("cheese", "pasta") 86 | .column(3).containsValues(6.80, 10.99); 87 | 88 | } 89 | 90 | 91 | @Test public void 92 | should_generate_insert_statements_from_update_containing_where_columnnames_values_positions_are_swapped() { 93 | 94 | // GIVEN 95 | TestTable foodTable = 96 | buildUniqueTable(DATA_SOURCE 97 | , "Food" 98 | , " id bigint" 99 | + ", Dishname varchar(255)" 100 | + ", Allergy varchar(255)" 101 | + ", Price decimal") 102 | .create() 103 | .insertValues("1, 'Spaghetti Bolognese', 'cheese', 6.80") 104 | .insertValues("2, 'Pizza', 'pasta', 10.99"); 105 | 106 | // WHEN 107 | String foodTableName = foodTable.getTableName(); 108 | String updateQuery = "UPDATE " + foodTableName + " SET Price = 7.00" 109 | + " WHERE Allergy LIKE 'past%' OR 'cheese' = Allergy" 110 | + " OR 'Pizza' = Dishname"; 111 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 112 | String insertScript = quickSqlTestData.generateInsertScriptFor(updateQuery); 113 | 114 | // THEN 115 | foodTable.recreate(); 116 | SQL_EXECUTOR.execute(insertScript); 117 | assertThat(foodTable).withScript(insertScript) 118 | .hasNumberOfRows(2) 119 | .column(0).hasOnlyNullValues() 120 | .column(1).containsValues("Spaghetti Bolognese", "Pizza") 121 | .column(2).containsValues("cheese", "pasta") 122 | .column(3).containsValues(6.80, 10.99); 123 | 124 | } 125 | 126 | @Test public void 127 | should_generate_insert_statement_from_update_containing_where_like_and() { 128 | 129 | // GIVEN 130 | TestTable foodTable = 131 | buildUniqueTable(DATA_SOURCE 132 | , "Food" 133 | , " id bigint" 134 | + ", Dishname varchar(255)" 135 | + ", Allergy varchar(255)" 136 | + ", Price decimal") 137 | .create() 138 | .insertValues("1, 'Spaghetti Bolognese', 'cheese', 6.80") 139 | .insertValues("2, 'Pizza', 'pasta', 10.99"); 140 | 141 | // WHEN 142 | String foodTableName = foodTable.getTableName(); 143 | String updateQuery = "UPDATE " + foodTableName + " SET Price = 7.00" 144 | + " WHERE Allergy LIKE 'past%'" 145 | + " AND Dishname = 'Pizza'"; 146 | QuickSqlTestData quickSqlTestData = QuickSqlTestData.buildFrom(DATA_SOURCE); 147 | String insertScript = quickSqlTestData.generateInsertScriptFor(updateQuery); 148 | 149 | // THEN 150 | foodTable.recreate(); 151 | SQL_EXECUTOR.execute(insertScript); 152 | assertThat(foodTable).withScript(insertScript) 153 | .hasNumberOfRows(1) 154 | .row(0).hasValues(null, "Pizza", "pasta", 10.99); 155 | 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/test/resources/junit-platform.properties: -------------------------------------------------------------------------------- 1 | junit.jupiter.displayname.generator.default = \ 2 | org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 19 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | --------------------------------------------------------------------------------